[英]Objective-c: why private ivars are not hidden from the outside access when using KVC
在尝试使用 KVC 访问 ivars 后,我注意到私有和受保护的 ivars 没有任何保护。 我在 ivar(私有或受保护的关键字)前面放什么并不重要 - 当使用 KVC 方法“setValue”时,ivar 始终是公共 ivar。 这是我的代码,其中所有七个 ivars 和属性在 class 实例之外都是可更改的:
//************ 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,它仍然可以在 class 之外更改。 那么我该如何强制执行封装并“保护我的 ivars 免受其他恶意程序员的伤害”:)
NSObject 符合NSKeyValueCoding非正式协议。 这定义了setValue:forKey:
和valueForKey:
。 setValue:forKey:
和valueForKey:
根据特定的搜索规则搜索访问键值的方法,包括直接访问实例变量。 这种直接访问由accessInstanceVariablesDirectly
方法控制,该方法是 NSKeyValueCoding 非正式协议的一部分,默认情况下返回YES
,允许这些方法直接访问实例变量,因此不会真正将它们设为私有。 它们仍然是私有的,无法直接访问。
要解决这个问题,您必须重写上面提到的方法并在NSKeyValueCoding
非正式协议中定义以防止它们访问。
正如 Abizern 所提到的,私有变量的属性仍然可以访问,因为 Objective-C 没有私有方法的概念。
如果您真的希望 iVar 保持私有,请不要为 iVar 声明@property
。
不再是私有的不是 iVar。 Objective-C 运行时没有私有方法的概念。 由于使用@property
和@synthesize 会生成符合KVC 的访问synthesize
方法,因此无论支持iVar 是否为私有,您始终可以调用这些方法。
但它并没有你想的那么糟糕。 使用您拥有的方法不会直接更改 iVar - 它会通过设置器。 如果您需要额外的保护,您可以编写自己的设置器来实现您需要的任何保护。
如果您只是将 iVar 声明为@private
并且不使其符合 KVC - 它将保持私有。 当然; 然后,您就不能在该 iVar 上使用 KVC 或 KVO,但如果您希望能够使用它们,则不应将其声明为私有 iVar。
今天,我注意到了一件有趣的事情。 Stephen Kochan, in his "Programming in Objective c 2.0" book, states one interesting fact about obj-c and c relation: "When you define a new class and its instance variables, those instance variables are actually stored inside a structure". 因此,可以使用 -> 运算符直接访问此类 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 机制仍然允许访问 class 之外的私有或公共 ivars,因此必须通过覆盖在 NSKeyValueCoding 非正式协议中声明并默认在 NSObject 中实现的 setValue:forKey: 和 valueForKey: 方法来显式执行真正的封装和保护
我会在这个老问题上加两分钱。
我认为@private
, @protected
也可以防止使用'->'运算符访问变量。
想象一下,您有一个名为myPrivateVar
的 iVar,声明如下:
@interface MyClass:NSObject{
@public
NSArray *myPrivateVar;
}
因此,即使您实现以下 class 方法以返回NO
并且没有为 iVar 声明访问器:
+accessInstanceVariablesDirectly{
return NO;
}
如果您使用myClassObj->myPrivateVar
,该变量仍然可以访问;
另一方面,如果您只是将@public
设置为@private
并且不实现accessInstanceVariableDirectly
,则仍然可以使用 KVC 访问该变量:
[myClassObj valueForKey:@"myPrivateVar"];
(并且无法通过myClassObj->myPrivateVar
访问)
因此,要使您的 iVar 完全私有,应将其声明为@private
并且还应实施accessInstanceVariablesDirectly
以返回NO
。
通过覆盖 kvc 条目来禁止:
@implementation MONObject
- (id)valueForKey:(NSString *)key
{
/* enforce it */
return nil;
}
- (void)setValue:(id)value forKey:(NSString *)key
{
/* enforce it */
}
/* and so on */
@end
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.