Objective-c: why private ivars are not hidden from the outside access when using KVC

孤街醉人 提交于 2019-11-29 02:20:54

NSObject conforms to the NSKeyValueCoding informal protocol. This defines setValue:forKey: and valueForKey:. setValue:forKey: and valueForKey: search for a way to access the value of the key according to specific search rules which includes directly accessing the instance variable. This direct accessing is controlled by accessInstanceVariablesDirectly method which is a part of the NSKeyValueCoding informal protocol, which by default returns YES, allowing those methods to directly access the instance variables and as a result not really making them private as such. They are still private from direct access.

To resolve this, you will have have to override the methods mentioned above and defined in the NSKeyValueCoding informal protocol to prevent their access.

As mentioned by Abizern, properties for private variables are still accessible since Objective-C has no concept of private methods.

Don't declare a @property for an iVar if you really want it to remain private.

It isn't the iVar that is no longer private. The Objective-C runtime doesn't have a concept of private methods. Since using @property and @synthesize generates KVC compliant accessor methods, you can always call the methods, regardless of whether the backing iVar is private or not.

But it isn't as bad as you think. Using the methods you have doesn't directly change the iVar - it goes through the setters. If you need extra protection you can write your own setter that implements whatever protection you need.

If you just declare an iVar as @private and don't make it KVC compliant - it will remain private. Of course; you then can't use KVC or KVO on that iVar, but if you wanted to be able to use them you shouldn't be declaring it as a private iVar.

Today, I have noticed interesting thing. 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". Therefore, the direct access to such ivar could be made using -> operator. So, finaly, I found where such keywords as @private and @protected really matters. If I try directly to change public ivar value in the main program using then everything is ok - the value will be changed. But if I try changing private ivar - then the compiler will warn me about private_num being a private ivar

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

But since default KVC mechanism still allows access to private or public ivars outside the class, the real encapsulation and protection must be explicitly enforced by overriding setValue:forKey: and valueForKey: methods that are declared in the NSKeyValueCoding informal protocol and by default implemented in NSObject

I'll add my two cents to this old question.

I think the @private, @protected is there also to prevent access to a variable using the '->' operator.

Imagine you have a iVar called myPrivateVar declared like below:

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

So even if you implement the below class method to return NO and don't have accessors declared for the iVar:

+accessInstanceVariablesDirectly{
    return NO;
}

the variable is still accessible if you use myClassObj->myPrivateVar;

On the other hand if you just make the @public to @private and don't implement accessInstanceVariableDirectly, the variable is still accessible by using KVC:

[myClassObj valueForKey:@"myPrivateVar"];

(and not accessible via myClassObj->myPrivateVar)

So to make your iVar completely private it should be declared as @private and also accessInstanceVariablesDirectly should be implemented to return NO.

forbid via an override of the kvc entries:

@implementation MONObject

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

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

/* and so on */

@end
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!