Objective-c:为啥在使用 KVC 时私有 ivars 不被外部访问隐藏
Posted
技术标签:
【中文标题】Objective-c:为啥在使用 KVC 时私有 ivars 不被外部访问隐藏【英文标题】:Objective-c: why private ivars are not hidden from the outside access when using KVCObjective-c:为什么在使用 KVC 时私有 ivars 不被外部访问隐藏 【发布时间】:2011-09-01 14:50:38 【问题描述】:在尝试使用 KVC 访问 ivars 后,我注意到私有和受保护的 ivars 没有任何保护。我在 ivar(私有或受保护的关键字)前面放什么并不重要 - 当使用 KVC 方法“setValue”时,ivar 始终是公共 ivar。这是我的代码,其中所有七个 ivars 和属性都可以在类实例之外更改:
//************ interface file ***************//
@interface MyClass : NSObject
@public
NSNumber *public_num;
@protected
NSNumber *protected_num;
@private
NSNumber *private_num;
NSNumber *private_property;
@property (retain) NSNumber *public_property;
@property (retain) NSNumber *private_property;
@end
//********* implementation file *********//
@interface MyClass()
@private
NSNumber *very_private_num;
@property (retain) NSNumber *very_private_property;
@end
@implementation MyClass
@synthesize public_property, private_property, very_private_property;
@end
//****** main **********//
MyClass *myClass = [[MyClass alloc] init];
[myClass setValue:[NSNumber numberWithInt:1] forKey:@"public_num"];
[myClass setValue:[NSNumber numberWithInt:2] forKey:@"protected_num"];
[myClass setValue:[NSNumber numberWithInt:3] forKey:@"private_num"];
[myClass setValue:[NSNumber numberWithInt:4] forKey:@"public_property"];
[myClass setValue:[NSNumber numberWithInt:5] forKey:@"private_property"];
[myClass setValue:[NSNumber numberWithInt:6] forKey:@"very_private_num"];
[myClass setValue:[NSNumber numberWithInt:7] forKey:@"very_private_property"];
NSNumber *l_public_num = [myClass valueForKey:@"public_num"];
NSNumber *l_protected_num = [myClass valueForKey:@"protected_num"];
NSNumber *l_private_num = [myClass valueForKey:@"private_num"];
NSNumber *l_public_property = [myClass valueForKey:@"public_property"];
NSNumber *l_private_property = [myClass valueForKey:@"private_property"];
NSNumber *l_very_private_num = [myClass valueForKey:@"very_private_num"];
NSNumber *l_very_private_property = [myClass valueForKey:@"very_private_property"];
NSLog(@"public_num = %@, protected_num = %@, private_num = %@, public_property = %@, private_property = %@, very_private_num = %@, very_private_property = %@", l_public_num, l_protected_num, l_private_num, l_public_property, l_private_property, l_very_private_num, l_very_private_property);
输出结果> public_num = 1, protected_num = 2, private_num = 3, public_property = 4, private_property = 5,very_private_num = 6,very_private_property = 7。
即使在私有接口上声明了 ivar,它仍然可以在类外更改。那么我该如何强制封装并“保护我的 ivars 免受其他程序员的伤害”:)
【问题讨论】:
到目前为止,我注意到拥有“某种私有方法”的唯一方法是在私有接口中声明一个纯方法(不使用合成)。然后编译器警告找不到方法。但仍然是代码并且该方法是可执行的:)。我在 hiddenMethod 中编写了 NSLog(@"Inside hiddenMethod\n") ,然后从 main 执行该方法,并在调试窗口中收到消息。 Objective-C 中没有私有方法。 【参考方案1】:如果您真的希望 iVar 保持私有,请不要为 iVar 声明 @property
。
不再是私有的不是 iVar。 Objective-C 运行时没有私有方法的概念。由于使用 @property
和 @synthesize
会生成符合 KVC 的访问器方法,因此无论支持的 iVar 是否为私有,您始终可以调用这些方法。
但它并没有你想象的那么糟糕。使用您拥有的方法不会直接更改 iVar - 它会通过设置器。如果您需要额外的保护,您可以编写自己的 setter 来实现您需要的任何保护。
如果您只是将 iVar 声明为 @private
并且不使其符合 KVC - 它将保持私有状态。当然;然后,您就不能在该 iVar 上使用 KVC 或 KVO,但如果您希望能够使用它们,则不应将其声明为私有 iVar。
【讨论】:
抱歉给我带来了麻烦。我的意思是,我放了唯一没有@property 和@synthesize 的私有ivar。尽管如此,还是可以使用 KVC 方法 setValue 从外部访问 ivar,并显示 11 的输出。 首先不要将其声明为@property
。不要为该 iVar 实现 Setter 和 getter。这里是quick guide to KVC
我将完整代码放在第一条评论中,没有@property 和@synthesize。而且我没有为那个 ivar 实现 setter 和 getter。不过,我可以使用 setValue 访问它。那么诀窍在哪里:)
注意:答案完全错误。如果继承自 NSObject,则该类符合 KVC。 KVC 将在默认情况下找不到属性的访问器方法时直接访问相应的实例变量。如果这不是您想要的,请在+ accessInstanceVariablesDirectly
中返回 NO
另外,声明为@private
是没用的。【参考方案2】:
通过覆盖 kvc 条目来禁止:
@implementation MONObject
- (id)valueForKey:(NSString *)key
/* enforce it */
return nil;
- (void)setValue:(id)value forKey:(NSString *)key
/* enforce it */
/* and so on */
@end
【讨论】:
谢谢。恕我直言,我认为 obj-c 没有真正的私有机制。模拟所需的隐私性并覆盖编译器(或运行时?)的默认行为是一个变通方法的问题。但是,奇怪的是为什么在声明中使用“private”和“protected”这些关键字,如果它们没有真正的影响。我认为他们让人们感到困惑,尤其是初学者。 恕我直言,程序员有能力通过自定义或合成的 setter 和 getter 使用属性来控制 ivars/properties 的值,从而遵循工作约定。恕我直言,封装仅用作工作约定。顺便问一下,您是否在实际项目中模拟/使用私有 ivars/属性/方法? 在需要的地方使用私有 c++ ivar 或私有类 我写的 c++ 比 objc 多——我为任务选择了正确的语言。【参考方案3】:NSObject 符合NSKeyValueCoding 非正式协议。这定义了setValue:forKey:
和valueForKey:
。 setValue:forKey:
和valueForKey:
根据specific search rules 寻找访问键值的方法,包括直接访问实例变量。这种直接访问由 accessInstanceVariablesDirectly
方法控制,该方法是 NSKeyValueCoding 非正式协议的一部分,默认情况下返回 YES
,允许这些方法直接访问实例变量,因此不会真正将它们设为私有。它们仍然是私有的,无法直接访问。
要解决此问题,您必须重写上述方法并在NSKeyValueCoding
非正式协议中定义,以防止其访问。
正如 Abizern 所提到的,私有变量的属性仍然可以访问,因为 Objective-C 没有私有方法的概念。
【讨论】:
谢谢。我还在努力学习obj-c,所以我认为KVC是直接访问方式(课外)... 怎么想,如果接口声明中没有真正的影响,为什么还要在接口声明中使用“private”和“protected”这些关键字?他们让人们感到困惑,尤其是初学者。此外,如果没有真正的“隐私”,为什么还有所谓的“私有接口”驻留在实现文件中? 我主要使用 Obj-C 和 C++,在这两种语言中,都有一些方法可以绕过访问说明符,例如 private 和 protected。实际上,这只是对使用该类的开发人员的指导或指示,他们不应该接触任何未标记为公共的东西。虽然这个示例是一个库解决方案,但也没有理由为什么使用具有易于修改标头的库的人不能完全从标头中删除 @private。【参考方案4】:今天,我注意到有趣的事情。 Stephen Kochan 在他的“Objective c 2.0 编程”一书中,陈述了关于 obj-c 和 c 关系的一个有趣事实:“当您定义一个新类及其实例变量时,这些实例变量实际上存储在一个结构中”。因此,可以使用 -> 运算符直接访问此类 ivar。所以,最后,我发现了像 @private 和 @protected 这样的关键字真正重要的地方。如果我尝试直接在主程序中使用更改公共 ivar 值,那么一切正常 - 值将被更改。但是如果我尝试更改私有 ivar - 那么编译器会警告我 private_num 是私有 ivar
myClass->public_num = [NSNumber numberWithInt:11];
myClass->private_num = [NSNumber numberWithInt:11]; // compiler will complain and reject the compilation
但由于默认的 KVC 机制仍然允许访问类外的私有或公共 ivars,因此必须通过覆盖在 NSKeyValueCoding 非正式协议中声明的 setValue:forKey: 和 valueForKey: 方法来显式执行真正的封装和保护,并且默认情况下在 NSObject 中实现
【讨论】:
【参考方案5】:我会在这个老问题上加两分钱。
我认为@private
、@protected
也可以防止使用“->”运算符访问变量。
假设您有一个名为 myPrivateVar
的 iVar,声明如下:
@interface MyClass:NSObject
@public
NSArray *myPrivateVar;
因此,即使您实现以下类方法以返回 NO
并且没有为 iVar 声明访问器:
+accessInstanceVariablesDirectly
return NO;
如果您使用 myClassObj->myPrivateVar
,该变量仍然可以访问;
另一方面,如果您只是将@public
设置为@private
而没有实现accessInstanceVariableDirectly
,则仍然可以使用KVC 访问该变量:
[myClassObj valueForKey:@"myPrivateVar"];
(不能通过myClassObj->myPrivateVar
访问)
因此,要使您的 iVar 完全私有,应将其声明为 @private
,并且还应实现 accessInstanceVariablesDirectly
以返回 NO
。
【讨论】:
以上是关于Objective-c:为啥在使用 KVC 时私有 ivars 不被外部访问隐藏的主要内容,如果未能解决你的问题,请参考以下文章