iOS修改聲明為readonly的屬性值


本文討論的是,對於類中聲明為 readonly 的屬性值,我們就不可以修改其值了么?如何可以,那么如何修改呢?

為了便於說明,定義一個 ACLStudent 的類:

ACLStudent.h

@interface ACLStudent : NSObject

@property (nonatomic, assign, readonly) NSInteger studentId;
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;

- (instancetype)initWithStudentId:(NSInteger)studentId firstName:(NSString *)firstName lastName:(NSString *)lastName;

@end

--------------------------

ACLStudent.m

@implementation ACLStudent

- (instancetype)initWithStudentId:(NSInteger)studentId firstName:(NSString *)firstName lastName:(NSString *)lastName {
    self = [super init];
    if (self) {
        _studentId = studentId;
        _firstName = [firstName copy];
        _lastName = [lastName copy];
    }
    
    return self;
}

@end

接下來定義一個 ACLStudent 類的對象:

ACLStudent *student = [[ACLStudent alloc] initWithStudentId:1 firstName:@"Carya" lastName:@"Liu"];
NSLog(@"student firstName: %@", student.firstName);

現在我們考慮的就是如何修改 student 對象的 firstName 屬性值為 @"Qiu" 。

如果直接調用 firstName 的 setter 方法, student.firstName = @"Qiu" , 那么就直接報錯,提示不能夠給聲明為 readonly 的屬性賦值。那么使用 KVC 呢?

[student setValue:@"Qiu" forKey:NSStringFromSelector(@selector(firstName))];
NSLog(@"student firstName after changed: %@", student.firstName);

運行,發現屬性值被成功修改。哈哈,那么現在來看看 KVC 為什么能夠修改該屬性值呢?看看文檔 Accessor Search Implementation Details 。

當使用 setValue:forKey: 來設置對象的屬性時,會以下面的優先順序來尋找對應的 key :

    消息接收對象會查找是否存在滿足 set<Key>: 格式的存取方法。
    如果不存在滿足條件的存取方法,且消息接收對象的類方法 + (BOOL)accessInstanceVariablesDirectly 返回 YES,那么該對象會以 _<key> , _is<Key> , <key> , is<Key> 的順序查找是否存在對應的key。
    如果存在對應的存取方法或者找到對應的實例變量,那么就會改變該 key 所對應的值 value。必要的話,value 所對應的值會從對象中解析出來,如 Representing Non-Object Values 所描述的那樣。
    如果沒有找到對應的存取方法或者實例變量,那么該消息對象的 setValue:forUndefinedKey: 將會調用。

對於上述第2點說明一下,如果我們不想讓 setValue:forKey: 方法改變對象的屬性值,那么重寫其類方法 + (BOOL)accessInstanceVariablesDirectly 返回 NO (該方法默認返回 YES,即在不存在滿足條件的存取方法時,允許直接訪問屬性對應的實例變量);在搜索實例變量時,會首先檢查帶下划線的實例變量,然后檢查不帶下划線的實例變量。

對於上述第3點舉例說明,如果修改 student 對象的屬性 NSInteger studentId , 注意其是 NSInteger 類型,我們在調用 setValue:forKey: 方法時可以像這樣

[student setValue:@(20) forKey:NSStringFromSelector(@selector(studentId))];

傳入一個 NSNumber 對象也可以,Objective-C 會處理好一切。

對於上面的示例,使用 setValue:forKey: 實際修改的是 student 實例中 _firstName 實例變量的值。不要忘記,我們在聲明一個 firstName 的屬性時,編譯器會為我們自動合成一個 _firstName 的實例變量。

總結:

    當我們聲明一個 readonly 的屬性,外部可能會通過 KVC 修改該屬性值。
    為了避免 KVC 修改屬性值,須將定義屬性所在類的類方法 + (BOOL)accessInstanceVariablesDirectly 重寫,使其返回 NO.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM