7

在尝试使用 KVC 访问 ivars 后,我注意到私有和受保护的 ivars 没有任何保护。我在 ivar(私有或受保护的关键字)前面放什么并不重要 - 当使用 KVC 方法“setValue”时,ivar 始终是公共 ivar。这是我的代码,其中所有七个 ivars 和属性在类实例之外都是可更改的:

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

即使在私有接口上声明了 ivar,它仍然可以在类外更改。那么我该如何强制封装并“保护我的 ivars 免受其他恶意程序员的伤害”:)

4

5 回答 5

10

NSObject 符合NSKeyValueCoding非正式协议。这定义了setValue:forKey:valueForKey:setValue:forKey:并根据特定的搜索规则搜索valueForKey:访问键值的方法,包括直接访问实例变量。这种直接访问由作为 NSKeyValueCoding 非正式协议一部分的方法控制,默认情况下返回,允许这些方法直接访问实例变量,因此不会真正将它们设为私有。它们仍然是私有的,无法直接访问。accessInstanceVariablesDirectlyYES

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

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

于 2011-05-25T10:57:18.110 回答
4

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

不再是私有的不是 iVar。Objective-C 运行时没有私有方法的概念。由于 using@property和 @synthesize生成符合 KVC 的访问器方法,因此您始终可以调用这些方法,而不管支持的 iVar 是否是私有的。

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

如果您只是将 iVar 声明为@private并且不使其符合 KVC - 它将保持私有。当然; 然后,您就不能在该 iVar 上使用 KVC 或 KVO,但如果您希望能够使用它们,则不应将其声明为私有 iVar。

于 2011-05-25T10:10:59.437 回答
3

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

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

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

于 2011-05-26T10:25:07.517 回答
1

我会在这个老问题上加两分钱。

我认为@private,@protected也可以防止使用 '->' 运算符访问变量。

想象一下,你有一个名为 iVar 的myPrivateVar声明如下:

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

因此,即使您实现以下类方法以返回NO并且没有为 iVar 声明访问器:

+accessInstanceVariablesDirectly{
    return NO;
}

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

另一方面,如果你只是做@publicto@private并且不实现accessInstanceVariableDirectly,该变量仍然可以通过使用 KVC 访问:

[myClassObj valueForKey:@"myPrivateVar"];

(不能通过 访问myClassObj->myPrivateVar

因此,要使您的 iVar 完全私有,应将其声明为@private并且accessInstanceVariablesDirectly还应实现为 return NO

于 2013-06-18T14:51:38.773 回答
0

通过覆盖 kvc 条目来禁止:

@implementation MONObject

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

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

/* and so on */

@end
于 2011-05-25T10:44:40.923 回答