KVC(Key Value Coding)
是定義在NSKeyValueCoding.h
文件中的一個(gè)非正式協(xié)議飘千。KVC
提供了一種可以間接訪問其屬性方法或成員變量的機(jī)制,可以通過字符串來訪問對(duì)應(yīng)的屬性方法或成員變量。
常用API
設(shè)值與取值
常用到的設(shè)值與取值的幾種方式:
// 通過Key來取值
- (nullable id)valueForKey:(NSString *)key;
- (void)setValue:(nullable id)value forKey:(NSString *)key;
// 通過KeyPath(也稱路由)來取值
- (nullable id)valueForKeyPath:(NSString *)keyPath;
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
KeyPath
使用示例入
[myAcount setValue:@"xiaoxiao" forKeyPath:@"model.name"]; // 可以通過 . 的形式獲取到更深層的屬性
NSLog(@"%@",[myAcount valueForKeyPath:@"model.name"]);
字典操作
- (void)dictionaryTest{
NSDictionary* dict = @{
@"name":@"xiaoxiao",
@"age":@18,
};
PeasonModel *p = [[PeasonModel alloc] init];
// 字典轉(zhuǎn)模型
[p setValuesForKeysWithDictionary:dict];
NSLog(@"%@",p);
// 鍵數(shù)組轉(zhuǎn)模型到字典
NSArray *array = @[@"name",@"age"];
NSDictionary *dic = [p dictionaryWithValuesForKeys:array];
NSLog(@"%@",dic);
}
其他常用方法
//默認(rèn)返回YES睁蕾,表示如果沒有找到Set<Key>方法的話戳表,會(huì)按照_key趣苏,_iskey离唬,key扰楼,iskey的順序搜索成員呀癣,設(shè)置成NO就不這樣搜索
+ (BOOL)accessInstanceVariablesDirectly;
//KVC提供屬性值正確性驗(yàn)證的API,它可以用來檢查set的值是否正確弦赖、為不正確的值做一個(gè)替換值或者拒絕設(shè)置新值并返回錯(cuò)誤原因项栏。
- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
//這是集合操作的API,里面還有一系列這樣的API蹬竖,如果屬性是一個(gè)NSMutableArray沼沈,那么可以用這個(gè)方法來返回。
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
//如果Key不存在案腺,且KVC無法搜索到任何和Key有關(guān)的字段或者屬性庆冕,則會(huì)調(diào)用這個(gè)方法康吵,默認(rèn)是拋出異常劈榨。
- (nullable id)valueForUndefinedKey:(NSString *)key;
//和上一個(gè)方法一樣,但這個(gè)方法是設(shè)值晦嵌。
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
//如果你在SetValue方法時(shí)面給Value傳nil同辣,則會(huì)調(diào)用這個(gè)方法
- (void)setNilValueForKey:(NSString *)key;
//輸入一組key,返回該組key對(duì)應(yīng)的Value,再轉(zhuǎn)成字典返回惭载,用于將Model轉(zhuǎn)到字典旱函。
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
設(shè)值&取值過程
1.設(shè)值
常用設(shè)值方法就是setValue:forKey
,其底層原理是怎么樣的呢?開發(fā)過程中描滔,我們可能曾經(jīng)試著Jump to Definition
去查看底層實(shí)現(xiàn)棒妨,然后只有注釋,Foundation
閉源的含长。想要窺探其底層券腔,可能需要其他手段:1.通過蘋果官方文檔伏穆;2.Github搜相關(guān)的工程;3.通過Hopper反匯編纷纫,通過偽代碼去理解枕扫;
官網(wǎng)Key-Value Coding
在官網(wǎng)中找到了以下關(guān)于setValue:forkey
的解釋
setValue:forkey
執(zhí)行流程是:
- 1.先尋找是否有
set<Key> or _set<Key>
,如果找到辱魁,就直接設(shè)置屬性value
.(key是指成員變量名烟瞧,首字符大小寫需要符合KVC的命名規(guī)范)); - 2.如果沒找到,就判斷
accessInstanceVariablesDirectly
返回YES染簇,就按照以下順序?qū)ふ乙粋€(gè)實(shí)例變量参滴,名稱類似_<key>,_is<Key>剖笙,<key>卵洗,或者is<Key>
,找到就直接賦值弥咪; - 3.如果返回為NO过蹂,則表明
setter
方法 或者 實(shí)例變量都沒有找到,系統(tǒng)會(huì)執(zhí)行該對(duì)象的setValue:forUndefinedKey:
方法聚至,默認(rèn)會(huì)拋出NSUndefinedKeyException類型的異常酷勺。
整理得到流程圖更方便理解
setValue:forKey: 流程
2.取值
當(dāng)然也就找到了valueForKey:
的解釋
取值-valueForKey 邏輯
流程是:
- 1.先找getter方法,按照順序依次查找:
get<Key> -> <key> -> is<Key> -> _<key>
扳躬,如果找到執(zhí)行第5脆诉,否者執(zhí)行第2; - 2.按順序次沒找著贷币,就會(huì)尋找
countOf <Key>和objectIn <Key> AtIndex :和<key> AtIndexes :
, 如果找到了countOf <Key>
或其他兩個(gè)中的一個(gè)击胜,則會(huì)創(chuàng)建一個(gè)響應(yīng)所有NSArray方法的集合代理對(duì)象,并返回該對(duì)象役纹,即NSKeyValueArray
偶摔,是NSArray的子類。否則執(zhí)行第3.
代理對(duì)象隨后將接收到的所有NSArray消息轉(zhuǎn)換為countOf<Key>
促脉,objectIn<Key> AtIndex:
和<key>AtIndexes:
消息的某種組合辰斋,用來創(chuàng)建鍵值編碼對(duì)象。如果原始對(duì)象還實(shí)現(xiàn)了一個(gè)名為get<Key>:range:之類的可選方法瘸味,則代理對(duì)象也將在適當(dāng)時(shí)使用該方法宫仗; - 3.上面方法沒找到,就會(huì)查找countOf <Key>旁仿,enumeratorOf<Key>和memberOf<Key>這三個(gè)方法藕夫;
如果找到所有的三個(gè)方法,則會(huì)創(chuàng)建一個(gè)響應(yīng)所有NSSet
方法的集合代理對(duì)象并將其返回。否則毅贮,請(qǐng)繼續(xù)執(zhí)行步驟4梭姓。
此代理對(duì)象隨后將其收到的所有NSSet消息轉(zhuǎn)換為countOf <Key>,enumeratorOf <Key>和memberOf <Key>:
消息的某種組合嫩码,以創(chuàng)建它的對(duì)象誉尖。實(shí)際上,代理對(duì)象與與鍵值編碼兼容的對(duì)象一起工作铸题,使基礎(chǔ)屬性的行為就好像它是NSSet一樣铡恕,即使它不是NSSet那樣。 - 4.如果沒有找到方法組丢间,并且接收方的類方法
accessInstanceVariablesDirectly
返回YES探熔,則按順序搜索名為_ <key>,_ is <Key>烘挫,<key>或is <Key>
的實(shí)例變量诀艰。如果找到,請(qǐng)直接獲取實(shí)例變量的值饮六,然后繼續(xù)執(zhí)行步驟5其垄。否則,請(qǐng)繼續(xù)執(zhí)行步驟6卤橄。 - 5.如果搜索到的屬性值的類型為指針绿满,則直接返回結(jié)果;
如果該值是NSNumber
支持的標(biāo)量類型窟扑,則將其存儲(chǔ)在NSNumber
實(shí)例中并返回它喇颁。
如果結(jié)果是NSNumber
不支持的標(biāo)量類型,則轉(zhuǎn)換為NSValue
對(duì)象并返回該對(duì)象嚎货。
如果所有其他方法均失敗橘霎,則調(diào)用valueForUndefinedKey:
。默認(rèn)情況下殖属,這會(huì)引發(fā)一個(gè)異常NSUndefinedKeyException
姐叁。
整理得到流程圖:
valueForKey: 取值流程
KVC應(yīng)用場(chǎng)景
1、動(dòng)態(tài)設(shè)值和取值
常用通過setValue:forKey:
和valueForKey:
, 或者setValue:forKeyPath:
和valueForKeyPath:
2忱辅、通過KVC訪問和修改私有變量
在外部定義的對(duì)象是無法直接訪問私有屬性的七蜘,但是可以通過KVC修改和訪問任何私有屬性3谭溉、多值操作(Model和字典互轉(zhuǎn))
// 字典轉(zhuǎn)模型
- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;
// 模型轉(zhuǎn)字典
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
- 4墙懂、修改一些系統(tǒng)空間的內(nèi)部屬性
很多UI控件都是在其內(nèi)部由多個(gè)UI控件組合而成,使用KVC可以訪問到底層的API扮念,常用的就是自定義Tabbar
损搬、個(gè)性化UITextField
中的placeHolderText
。