一、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:
原理
- 查找是否實現
setter
方法,如果有,優先調用setter
方法完成賦值(注意:set
后面的鍵的第一字字母必須是大寫!!) - 當沒找到
setter
方法,調用accessInstanceVariablesDirectly
詢問;如果返回YES
,順序匹配變量名與_<key>
,_is<Key>
,<key>
,is<Key>
,匹配到則設定其值,如果返回NO
,結束查找,并調用setValue:forUndefinedKey:
報異常 - 如果既沒有
setter
也沒有實例變量時,調用setValue:forUndefinedKey:
setValue:forKey:原理
valueForKey:
的原理
-
kvc
取值按照getKey、key、iskey、_key
順序查找方法 - 存在直接調用
- 沒找到同樣,先查看
accessInstanceVariablesDirectly
方法 - 如果可以訪問會按照
_key、_isKey、key、iskey
的順序查找成員變量 - 找到直接取值
- 如果所有情況都失敗,則調用
valueForUndefinedKey:
方法并拋出NSUnkonwKeyException
異常,這是默認行為,但是子類可以重寫此方法。
三、搜索規則
NSMutableArray 、NSMutableSet
和NSMutableOrderedSet
對應的搜索規則
以NSMutableSet
為例說明:
- (NSMutableSet *)mutableSetValueForKey:(NSString *)key;
搜索
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>:
。如果
receiver
是ManagedObject
,那么就不會繼續搜索如果上一步的方法沒有找到,則搜索
set<Key>:
格式的方法,如果找到,那么發送給代理集合的NSMutableSet
最終都會調用set<Key>:
方法。如果上一步的方法還沒有找到,再檢查類方法
+ (BOOL) accessInstanceVariablesDirectly
,如果返回YES
(默認行為),會按_<key>,<key>
,的順序搜索成員變量名,如果找到,那么發送的NSMutableSet
消息方法直接交給這個成員變量處理。如果還是找不到,則調用
valueForUndefinedKey:
。
四、適用場景
- 動態取值與設值
setValue:forKeyPath:
和valueForKeyPath:
可以嵌套層級setObject:forKey:
和ObjectForKey:
中的key
可以不為NSString
setObject:forKey:
中的object
不能為空,但是另外兩者可以
- 字典轉模型
- 訪問或修改私有變量,iOS13之后蘋果限制了此方法
例如在
.m
中定義的私有成員變量和屬性,都可以通過KVC
的方式訪問。
這個操作對readonly
的屬性,@protected
的成員變量,都可以正常訪問。如果不想讓外界訪問類的成員變量,則可以將accessInstanceVariablesDirectly
屬性賦值為NO
。
- 修改一些控件的內部屬性
眾所周知很多
UI
控件都由很多內部UI
控件組合而成的,但是Apple
度沒有提供這訪問這些控件的API
,這樣我們就無法正常地訪問和修改這些控件的樣式。而KVC
在大多數情況可下可以解決這個問題。
五、KVC異常處理
-
key
或者keyPath
發生錯誤
當根據
KVC
搜索規則,沒有搜索到對應的key
或者keyPath
,則會調用對應的異常方法。異常方法的默認實現,在異常發生時會拋出一個NSUndefinedKeyException
的異常,并且應用程序Crash
我們可以重寫下面兩個方法(這種方法只能運用在自己類的對象中):
- (nullable id)valueForUndefinedKey:(NSString *)key;
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
- 傳參為
nil
通常情況下,
KVC
不允許你要在調用setValue:
屬性值forKey:
(或者keyPath
)時對非對象傳遞一個nil
的值;如果你不小心傳了,KVC
會調用setNilValueForKey:
方法。這個方法默認是拋出異常,所以一般而言最好還是重寫這個方法。
我們可以重寫這個方法:
-(void)setNilValueForKey:(NSString *)key{
NSLog(@"不能將%@設成nil",key);
}
- 使用
swizzling