繁体   English   中英

Objective-c:为什么在使用 KVC 时私有 ivars 不会被外部访问隐藏

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM