简体   繁体   English

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

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

After trying to access ivars using KVC, I have noticed that there was no protection on private and protected ivars.在尝试使用 KVC 访问 ivars 后,我注意到私有和受保护的 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".我在 ivar(私有或受保护的关键字)前面放什么并不重要 - 当使用 KVC 方法“setValue”时,ivar 始终是公共 ivar。 Here is my code where all of the seven ivars and properties are changeble outside the class instance:这是我的代码,其中所有七个 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);

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.输出结果> 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.即使在私有接口上声明了 ivar,它仍然可以在 class 之外更改。 So how do I have to enforce encapsulation and "to protect my ivars from evil other programmers":)那么我该如何强制执行封装并“保护我的 ivars 免受其他恶意程序员的伤害”:)

NSObject conforms to the NSKeyValueCoding informal protocol. NSObject 符合NSKeyValueCoding非正式协议。 This defines setValue:forKey: and valueForKey: .这定义了setValue:forKey: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. setValue:forKey:valueForKey:根据特定的搜索规则搜索访问键值的方法,包括直接访问实例变量。 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.这种直接访问由accessInstanceVariablesDirectly方法控制,该方法是 NSKeyValueCoding 非正式协议的一部分,默认情况下返回YES ,允许这些方法直接访问实例变量,因此不会真正将它们设为私有。 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.要解决这个问题,您必须重写上面提到的方法并在NSKeyValueCoding非正式协议中定义以防止它们访问。

As mentioned by Abizern, properties for private variables are still accessible since Objective-C has no concept of private methods.正如 Abizern 所提到的,私有变量的属性仍然可以访问,因为 Objective-C 没有私有方法的概念。

Don't declare a @property for an iVar if you really want it to remain private.如果您真的希望 iVar 保持私有,请不要为 iVar 声明@property

It isn't the iVar that is no longer private.不再是私有的不是 iVar。 The Objective-C runtime doesn't have a concept of private methods. Objective-C 运行时没有私有方法的概念。 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.由于使用@property和@synthesize 会生成符合KVC 的访问synthesize方法,因此无论支持iVar 是否为私有,您始终可以调用这些方法。

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.使用您拥有的方法不会直接更改 iVar - 它会通过设置器。 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.如果您只是将 iVar 声明为@private并且不使其符合 KVC - 它将保持私有。 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.然后,您就不能在该 iVar 上使用 KVC 或 KVO,但如果您希望能够使用它们,则不应将其声明为私有 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". 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.因此,可以使用 -> 运算符直接访问此类 ivar。 So, finaly, I found where such keywords as @private and @protected really matters.所以,最后,我发现了像 @private 和 @protected 这样的关键字真正重要的地方。 If I try directly to change public ivar value in the main program using then everything is ok - the value will be changed.如果我尝试直接在主程序中更改公共 ivar 值,那么一切正常 - 值将被更改。 But if I try changing private ivar - then the compiler will warn me about private_num being a private ivar但是如果我尝试更改私有 ivar - 那么编译器会警告我 private_num 是私有 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但是由于默认的 KVC 机制仍然允许访问 class 之外的私有或公共 ivars,因此必须通过覆盖在 NSKeyValueCoding 非正式协议中声明并默认在 NSObject 中实现的 setValue:forKey: 和 valueForKey: 方法来显式执行真正的封装和保护

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.我认为@private@protected也可以防止使用'->'运算符访问变量。

Imagine you have a iVar called myPrivateVar declared like below:想象一下,您有一个名为myPrivateVar的 iVar,声明如下:

@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:因此,即使您实现以下 class 方法以返回NO并且没有为 iVar 声明访问器:

+accessInstanceVariablesDirectly{
    return NO;
}

the variable is still accessible if you use myClassObj->myPrivateVar ;如果您使用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:另一方面,如果您只是将@public设置为@private并且不实现accessInstanceVariableDirectly ,则仍然可以使用 KVC 访问该变量:

[myClassObj valueForKey:@"myPrivateVar"];

(and not accessible via myClassObj->myPrivateVar ) (并且无法通过myClassObj->myPrivateVar访问)

So to make your iVar completely private it should be declared as @private and also accessInstanceVariablesDirectly should be implemented to return NO .因此,要使您的 iVar 完全私有,应将其声明为@private并且还应实施accessInstanceVariablesDirectly以返回NO

forbid via an override of the kvc entries:通过覆盖 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