[英]Objective-c: why private ivars are not hidden from the outside access when using KVC
在嘗試使用 KVC 訪問 ivars 后,我注意到私有和受保護的 ivars 沒有任何保護。 我在 ivar(私有或受保護的關鍵字)前面放什么並不重要 - 當使用 KVC 方法“setValue”時,ivar 始終是公共 ivar。 這是我的代碼,其中所有七個 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);
輸出結果> public_num = 1, protected_num = 2, private_num = 3, public_property = 4, private_property = 5,very_private_num = 6,very_private_property = 7。
即使在私有接口上聲明了 ivar,它仍然可以在 class 之外更改。 那么我該如何強制執行封裝並“保護我的 ivars 免受其他惡意程序員的傷害”:)
NSObject 符合NSKeyValueCoding非正式協議。 這定義了setValue:forKey:
和valueForKey:
。 setValue:forKey:
和valueForKey:
根據特定的搜索規則搜索訪問鍵值的方法,包括直接訪問實例變量。 這種直接訪問由accessInstanceVariablesDirectly
方法控制,該方法是 NSKeyValueCoding 非正式協議的一部分,默認情況下返回YES
,允許這些方法直接訪問實例變量,因此不會真正將它們設為私有。 它們仍然是私有的,無法直接訪問。
要解決這個問題,您必須重寫上面提到的方法並在NSKeyValueCoding
非正式協議中定義以防止它們訪問。
正如 Abizern 所提到的,私有變量的屬性仍然可以訪問,因為 Objective-C 沒有私有方法的概念。
如果您真的希望 iVar 保持私有,請不要為 iVar 聲明@property
。
不再是私有的不是 iVar。 Objective-C 運行時沒有私有方法的概念。 由於使用@property
和@synthesize 會生成符合KVC 的訪問synthesize
方法,因此無論支持iVar 是否為私有,您始終可以調用這些方法。
但它並沒有你想的那么糟糕。 使用您擁有的方法不會直接更改 iVar - 它會通過設置器。 如果您需要額外的保護,您可以編寫自己的設置器來實現您需要的任何保護。
如果您只是將 iVar 聲明為@private
並且不使其符合 KVC - 它將保持私有。 當然; 然后,您就不能在該 iVar 上使用 KVC 或 KVO,但如果您希望能夠使用它們,則不應將其聲明為私有 iVar。
今天,我注意到了一件有趣的事情。 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". 因此,可以使用 -> 運算符直接訪問此類 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 機制仍然允許訪問 class 之外的私有或公共 ivars,因此必須通過覆蓋在 NSKeyValueCoding 非正式協議中聲明並默認在 NSObject 中實現的 setValue:forKey: 和 valueForKey: 方法來顯式執行真正的封裝和保護
我會在這個老問題上加兩分錢。
我認為@private
, @protected
也可以防止使用'->'運算符訪問變量。
想象一下,您有一個名為myPrivateVar
的 iVar,聲明如下:
@interface MyClass:NSObject{
@public
NSArray *myPrivateVar;
}
因此,即使您實現以下 class 方法以返回NO
並且沒有為 iVar 聲明訪問器:
+accessInstanceVariablesDirectly{
return NO;
}
如果您使用myClassObj->myPrivateVar
,該變量仍然可以訪問;
另一方面,如果您只是將@public
設置為@private
並且不實現accessInstanceVariableDirectly
,則仍然可以使用 KVC 訪問該變量:
[myClassObj valueForKey:@"myPrivateVar"];
(並且無法通過myClassObj->myPrivateVar
訪問)
因此,要使您的 iVar 完全私有,應將其聲明為@private
並且還應實施accessInstanceVariablesDirectly
以返回NO
。
通過覆蓋 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.