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

问题描述:

在尝试使用KVC访问ivars后,我注意到在私有和受保护的ivars上没有保护。无论我在伊娃(私人或受保护的关键字)面前放置什么 - 在使用KVC方法“setValue”时,伊娃总是公共伊娃。这是我的代码,其中所有七个实例变量和性能是changeble类实例外:Objective-c:为什么私人ivars在使用KVC时不会被外部访问隐藏

//************ 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。

即使在专用界面声明的伊娃,它仍然可以在课堂外改变。那么我该如何执行封装并“保护我的ivars免受其他程序员的伤害”:)

+0

到目前为止,我已经注意到,“排序私有方法”的唯一方法是在私有接口中声明纯方法(不使用综合)。然后编译器警告未找到方法。但仍然代码和方法是可执行的:)。我在hiddenMethod中编写了NSLog(@“hidden hiddenMethod \ n”),然后从main执行该方法,并在调试窗口中获得了消息。 } – Centurion 2011-05-25 10:14:39

+0

Objective-C中没有私有方法。 – Abizern 2011-05-25 10:29:37

NSObject的符合NSKeyValueCoding非正式协议。这定义了setValue:forKey:valueForKey:setValue:forKey:valueForKey:根据specific search rules搜索访问密钥值的方式,其中包括直接访问实例变量。这种直接访问是由accessInstanceVariablesDirectly方法控制的,该方法是NSKeyValueCoding非正式协议的一部分,该协议默认返回YES,允许这些方法直接访问实例变量,因此不会真正使其成为私有的。他们仍然是直接访问的私人。

要解决这个问题,您必须重写上述方法并在NSKeyValueCoding非正式协议中定义,以防止他们访问。

正如Abizern所述,私有变量的属性仍然可以访问,因为Objective-C没有私有方法的概念。

+0

谢谢。我仍然在努力学习obj-c,因此我认为KVC是直接访问方式(在课堂以外)... – Centurion 2011-05-25 11:30:51

+2

我主要使用Obj-C和C++工作,并且在两种语言中都有避开访问说明符的方法,例如private和protected。真的,它只是开发人员使用该类的指南或指标,他们不应触及任何未标记为公开的内容。虽然这个例子是一个图书馆的工作,但是没有理由说为什么有人使用一个容易修改头文件的库不能从头文件中完全删除@private。 – 2011-05-25 12:16:56

如果您真的希望它保持私密状态,请不要为iVar声明@property

这不是不是私人的iVar。 Objective-C运行时没有私有方法的概念。由于使用@property和@synthesize会生成符合KVC标准的访问器方法,因此无论支持iVar是否为私有,您都可以调用方法。

但它并没有你想象的那么糟糕。使用你所使用的方法不会直接改变iVar - 它通过setter。如果你需要额外的保护,你可以编写自己的setter来实现你需要的任何保护。

如果您只是声明一个iVar为@private并且不符合KVC标准 - 它将保持私有状态。当然;那么你就不能在该iVar上使用KVC或KVO,但如果你想能够使用它们,则不应该将其声明为私有iVar。

+0

对不起。我的意思是,我把唯一的私人ivar没有@property和@synthesize。仍然可以使用KVC方法setValue从外部访问ivar,并显示11的输出。 – Centurion 2011-05-25 10:27:14

+0

不要声明它是一个“@属性”的开始。不要为该iVar实现Setter和getters。这里[KVC快速指南](http://developer.apple.com/library/ios/#documentation/general/conceptual/DevPedia-CocoaCore/KeyValueCoding.html) – Abizern 2011-05-25 10:28:37

+0

我把完整的代码放在第一个注释中,没有@property和@synthesize。而且我没有为那个伊娃实施制定者和获得者。不过,我可以通过setValue访问它。那么,诀窍在哪里:) – Centurion 2011-05-25 10:30:40

经由KVC条目的重写禁止:

@implementation MONObject 

- (id)valueForKey:(NSString *)key 
{ 
/* enforce it */ 
    return nil; 
} 

- (void)setValue:(id)value forKey:(NSString *)key 
{ 
/* enforce it */ 
} 

/* and so on */ 

@end 
+0

恕我直言,它是一个程序员遵循工作惯例的能力,通过使用属性通过自定义或合成的setter和getter来控制ivars /属性的值。恕我直言,封装仅用作工作惯例。顺便说一句,你是否在你的真实项目中模拟/使用私有的ivars/properties/methods? – Centurion 2011-05-25 10:58:25

+0

在你需要的地方使用私有的C++ ivar或私人类 – justin 2011-05-25 11:00:43

今天,我注意到有趣的事情。 Stephen Kochan在他的“Programming in Objective c 2.0”一书中陈述了一个关于obj-c和c关系的有趣事实:“当你定义一个新的类及其实例变量时,这些实例变量实际上存储在一个结构中”。因此,可以使用 - >运算符来直接访问这样的ivar。所以,最后,我发现@ private和@protected这些关键字真的很重要。如果我直接尝试在主程序中改变公共伊娃价值,那么一切都很好 - 价值将会改变。但是,如果我尝试改变私人伊娃 - 那么编译器会警告我private_num是一个私人伊娃

myClass->public_num = [NSNumber numberWithInt:11]; 
myClass->private_num = [NSNumber numberWithInt:11]; // compiler will complain and reject the compilation 

但由于默认KVC机制仍允许访问私人或公共实例变量的类外,真正的封装和保护必须通过覆盖setValue:forKey:和valueForKey:在NSKeyValueCoding非正式协议中声明并默认在NSObject中实现的方法来显式强制实施

我会将这两个美分添加到这个旧问题中。

我认为@private,@protected也有阻止使用' - >'操作符访问变量。

假设你有一个叫伊娃宣布像下面myPrivateVar

@interface MyClass:NSObject{ 
    @public 
    NSArray *myPrivateVar; 
} 

所以,即使你实现以下类方法返回NO,并没有申报伊娃存取:

+accessInstanceVariablesDirectly{ 
    return NO; 
} 

如果您使用myClassObj->myPrivateVar,该变量仍可访问;

在另一方面,如果你只是使@public@private并没有实现accessInstanceVariableDirectly,变量仍然是通过KVC访问:

[myClassObj valueForKey:@"myPrivateVar"]; 

(而不是通过访问myClassObj->myPrivateVar

因此,要使您的iVar完全隐私,应声明为@private,并且还应执行accessInstanceVariablesDirectly以返回NO