簡體   English   中英

Objective-c:為什么在使用 KVC 時私有 ivars 不會被外部訪問隱藏

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM