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

5

7

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" :)

Ironbound answered 25/5, 2011 at 9:42 Comment(2)
So far, I have noticed that the only way to have "sort of a private method" is to declare a pure method in private interface (without using synthesize). Then compiler warns about method being not found. But still code and the method is executable :). I wrote NSLog(@"Inside hiddenMethod\n") in the hiddenMethod and then executed that method from the main and I got the message in debug window. }Ironbound
There are no private methods in Objective-C.Insight
M
10

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.

Miscount answered 25/5, 2011 at 10:57 Comment(3)
Thanks. I'm still trying to learn obj-c, therefore I thought that KVC is the direct access way (outside the class)...Ironbound
What do think, why those keywords as "private" and "protected" are used in interface declaration if they have no real impact? They are confusing people, especially the begginers. Moreover, why there is so called "private interface" that resides in implementation file if there is no real "privatness"?Ironbound
I work primarily with Obj-C and C++ and in both languages there are ways to get around access specifiers such as private and protected. Really it is just a guide or indicator to developers using that class that they should not touch anything that isn't marked as public. Whilst this example is a library work around, there is also no reason why someone using a library that has easily modifiable headers cannot just remove @private from the header altogether.Miscount
I
4

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.

Insight answered 25/5, 2011 at 10:10 Comment(5)
Sorry for the mess. I mean, I put the only private ivar with no @property and @synthesize. Still, ivar is accessable from outside using KVC method setValue, and the output of 11 is shown.Ironbound
Don't declare it as a @property for a start. Don't implement Setters and getters for that iVar. Here a quick guide to KVCInsight
I put the full code in the first comment with no @property and @synthesize. And I didn't implement setter and getter for that ivar. Still, I can access it with setValue. So where is the trick :)Ironbound
ATTENTION: The answer totally wrong. If inherit from NSObject, the class is KVC compliant. KVC will access the corresponding instance variable directly on finding no accessor method for a property by default. If this is not what you want, return NO in + accessInstanceVariablesDirectlyEffusion
Also, declare as @private is useless.Effusion
I
3

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

Ironbound answered 26/5, 2011 at 10:25 Comment(0)
U
1

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.

Uncritical answered 18/6, 2013 at 14:51 Comment(0)
T
0

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
Twilatwilight answered 25/5, 2011 at 10:44 Comment(4)
Thanks. IMHO, I would conclude that obj-c has no real private mechanism. It is a matter of workarounds to simulate the desirable privatness and to override the default behaviour of the compiler (or runtime?). But then, it is strange why those keywords as "private" and "protected" are used in declaration if they have no real impact. I think they are confusing people, especially the begginers.Ironbound
IMHO, it is the competency of a programmer to follow a working conventions by using properties via custom or synthesized setters and getters to control the values of ivars/properties. IMHO, the encapsulation is used only as a working convention. By the way, do you simulate / use private ivars / properties / methods in your real projects?Ironbound
use a private c++ ivar, or private class where you need itTwilatwilight
i write more c++ than objc - i choose the right language for the task.Twilatwilight

© 2022 - 2024 — McMap. All rights reserved.