KVC(Key-Value Coding)

一、KVC簡介

KVC:鍵值編碼,使用字符串直接訪問對象的屬性

常用方法

@interface NSObject(NSKeyValueCoding)
 
- (nullable id)valueForKey:(NSString *)key;                          //直接通過Key來取值
- (void)setValue:(nullable id)value forKey:(NSString *)key;          //通過Key來設值
- (nullable id)valueForKeyPath:(NSString *)keyPath;                  //通過KeyPath來取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;  //通過KeyPath來設值
 
@end

NSKeyValueCoding其他方法

+ (BOOL)accessInstanceVariablesDirectly;
//默認返回YES,表示如果沒有找到Set<Key>方法的話,會按照_key,_iskey,key,iskey的順序搜索成員,設置成NO就不這樣搜索
 
- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
//KVC提供屬性值正確性?驗證的API,它可以用來檢查set的值是否正確、為不正確的值做一個替換值或者拒絕設置新值并返回錯誤原因。
 
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
//這是集合操作的API,里面還有一系列這樣的API,如果屬性是一個NSMutableArray,那么可以用這個方法來返回。
 
- (nullable id)valueForUndefinedKey:(NSString *)key;
//如果Key不存在,且沒有KVC無法搜索到任何和Key有關的字段或者屬性,則會調用這個方法,默認是拋出異常。
 
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
//和上一個方法一樣,但這個方法是設值。
 
- (void)setNilValueForKey:(NSString *)key;
//如果你在SetValue方法時面給Value傳nil,則會調用這個方法
 
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
//輸入一組key,返回該組key對應的Value,再轉成字典返回,用于將Model轉到字典。

二、實現原理

setValue:forKey:原理

  1. 查找是否實現setter方法,如果有,優先調用setter方法完成賦值(注意:set后面的鍵的第一字字母必須是大寫!!)
  2. 當沒找到setter方法,調用accessInstanceVariablesDirectly詢問;如果返回YES,順序匹配變量名與_<key>_is<Key>,<key>,is<Key>,匹配到則設定其值,如果返回NO,結束查找,并調用 setValue:forUndefinedKey:報異常
  3. 如果既沒有setter也沒有實例變量時,調用 setValue:forUndefinedKey:
    setValue:forKey:原理

valueForKey:的原理

  1. kvc取值按照 getKey、key、iskey、_key順序查找方法
  2. 存在直接調用
  3. 沒找到同樣,先查看accessInstanceVariablesDirectly方法
  4. 如果可以訪問會按照_key、_isKey、key、iskey的順序查找成員變量
  5. 找到直接取值
  6. 如果所有情況都失敗,則調用valueForUndefinedKey:方法并拋出NSUnkonwKeyException異常,這是默認行為,但是子類可以重寫此方法。
valueForKey:原理

三、搜索規則

NSMutableArray 、NSMutableSetNSMutableOrderedSet對應的搜索規則
NSMutableSet為例說明:

- (NSMutableSet *)mutableSetValueForKey:(NSString *)key;
NSMutableSet原理
  1. 搜索addObject<Key>Object: , remove<Key>Object:或者 add<Key> , remove<Key>格式的方法
    如果至少找到一個insert方法和一個remove方法,那么同樣返回一個可以響應NSMutableSet所有方法代理集合(類名是NSKeyValueFastMutableSet2),那么給這個代理集合發送NSMutableSet的方法,以addObject<Key>Object: , remove<Key>Object:或者 add<Key> , remove<Key>組合的形式調用。還有兩個可選實現的接口:intersect<Key> , set<Key>:

  2. 如果receiverManagedObject,那么就不會繼續搜索

  3. 如果上一步的方法沒有找到,則搜索set<Key>: 格式的方法,如果找到,那么發送給代理集合的NSMutableSet最終都會調用set<Key>:方法。

  4. 如果上一步的方法還沒有找到,再檢查類方法+ (BOOL) accessInstanceVariablesDirectly,如果返回YES(默認行為),會按_<key>,<key>,的順序搜索成員變量名,如果找到,那么發送的NSMutableSet消息方法直接交給這個成員變量處理。

  5. 如果還是找不到,則調用valueForUndefinedKey:

四、適用場景

  1. 動態取值與設值
  • setValue:forKeyPath:valueForKeyPath:可以嵌套層級
  • setObject:forKey:ObjectForKey:中的key可以不為NSString
  • setObject:forKey:中的object不能為空,但是另外兩者可以
  1. 字典轉模型
  2. 訪問或修改私有變量,iOS13之后蘋果限制了此方法

例如在.m中定義的私有成員變量和屬性,都可以通過KVC的方式訪問。
這個操作對readonly的屬性,@protected的成員變量,都可以正常訪問。如果不想讓外界訪問類的成員變量,則可以將accessInstanceVariablesDirectly屬性賦值為NO

  1. 修改一些控件的內部屬性

眾所周知很多UI控件都由很多內部UI控件組合而成的,但是Apple度沒有提供這訪問這些控件的API,這樣我們就無法正常地訪問和修改這些控件的樣式。而KVC在大多數情況可下可以解決這個問題。

五、KVC異常處理

  1. key或者keyPath發生錯誤

當根據KVC搜索規則,沒有搜索到對應的key或者keyPath,則會調用對應的異常方法。異常方法的默認實現,在異常發生時會拋出一個NSUndefinedKeyException的異常,并且應用程序Crash
我們可以重寫下面兩個方法(這種方法只能運用在自己類的對象中):

- (nullable id)valueForUndefinedKey:(NSString *)key;
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;

  1. 傳參為nil

通常情況下,KVC不允許你要在調用setValue:屬性值 forKey:(或者keyPath)時對非對象傳遞一個nil的值;如果你不小心傳了,KVC會調用setNilValueForKey:方法。這個方法默認是拋出異常,所以一般而言最好還是重寫這個方法。

我們可以重寫這個方法:

-(void)setNilValueForKey:(NSString *)key{
    NSLog(@"不能將%@設成nil",key);
}
  1. 使用swizzling
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容