After trying to access ivars using KVC, I have noticed that there was no protection on private and protected ivars. It doesn't matter what I put a in front of the ivar (private or protected keyword) - an ivar is always a public ivar when using KVC method "setValue". Here is my code where all of the seven ivars and properties are changeble outside the class instance:
//************ 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);
The result of the output> public_num = 1, protected_num = 2, private_num = 3, public_property = 4, private_property = 5, very_private_num = 6, very_private_property = 7.
Even if the ivar declared at private interface, it is still changeable outside the class. So how do I have to enforce encapsulation and "to protect my ivars from evil other programmers" :)
NSObject conforms to the NSKeyValueCoding informal protocol. This defines
setValue:forKey:
andvalueForKey:
.setValue:forKey:
andvalueForKey:
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 byaccessInstanceVariablesDirectly
method which is a part of the NSKeyValueCoding informal protocol, which by default returnsYES
, 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.
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
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
forbid via an override of the kvc entries:
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:So even if you implement the below class method to return
NO
and don't have accessors declared for the iVar: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 implementaccessInstanceVariableDirectly
, the variable is still accessible by using KVC:(and not accessible via
myClassObj->myPrivateVar
)So to make your iVar completely private it should be declared as
@private
and alsoaccessInstanceVariablesDirectly
should be implemented to returnNO
.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.