先上一波KVC的官方文檔谒所,
setValue:forKey:
的搜索流程:
-【第一步】首先查找是否有這三種setter方法廷臼,按照查找順序為set<Key>:-> _set<Key> -> setIs<Key>(文檔中沒有提到,后文會驗證
)
如果有其中任意一個setter方法,則直接設(shè)置屬性的value(主注意:key是指成員變量名则披,首字符大小寫需要符合KVC的命名規(guī)范)
如果都沒有搂誉,則進入【第二步】
-【第二步】:如果沒有第一步中的三個簡單的setter方法,則查找accessInstanceVariablesDirectly是否返回YES熟呛,
如果返回YES宽档,則查找間接訪問的實例變量進行賦值,查找順序為:_<key> -> _is<Key> -> <key> -> is<Key>
如果找到其中任意一個實例變量庵朝,則賦值
如果都沒有吗冤,則進入【第三步】
如果返回NO,則進入【第三步】
-【第三步】如果setter方法 或者 實例變量都沒有找到九府,系統(tǒng)會執(zhí)行該對象的setValue:forUndefinedKey:方法椎瘟,默認拋出NSUndefinedKeyException類型的異常
驗證一下第一步的問題:
定義如下類
@interface YPPerson ()
{
@public
NSString *name;
NSString *_name;
NSString *isName;
NSString *_isName;
}
@end
@implementation YPPerson
//挨個注釋以下set方法,確認setValue:forKey:是否會調(diào)用
- (void)setName:(NSString *)name
{
NSLog(@"%s",__func__);
}
- (void)_setName:(NSString *)name
{
NSLog(@"%s",__func__);
}
- (void)setIsName:(NSString *)name
{
NSLog(@"%s",__func__);
}
//不會調(diào)用這個
- (void)_setIsName:(NSString *)name
{
NSLog(@"%s",__func__);
}
通過以下代碼調(diào)用setValue:forKey:
YPPerson *person = [[YPPerson alloc]init];
[person setValue:@"YP" forKey:@"name"];
文檔中提到的setName
,_setName
就不驗證了侄旬,我們直接注釋掉降传,運行代碼,結(jié)果如下
證明官方文檔中確實少寫了這個方法勾怒。嘿嘿婆排,被逮住了吧声旺。
接下來看看valueForKey:
翻譯一下
-【第一步】首先查找getter方法,按照get<Key> -> <key> -> is<Key> -> _<key>的方法順序查找段只,
如果找到腮猖,則進入【第五步】
如果沒有找到,則進入【第二步】
-【第二步】如果第一步中的getter方法沒有找到赞枕,KVC會查找countOf <Key>和objectIn <Key> AtIndex :和<key> AtIndexes :
如果找到countOf <Key>和其他兩個中的一個澈缺,則會創(chuàng)建一個響應(yīng)所有NSArray方法的集合代理對象,并返回該對象炕婶,即NSKeyValueArray姐赡,是NSArray的子類。代理對象隨后將接收到的所有NSArray消息轉(zhuǎn)換為countOf<Key>柠掂,objectIn<Key> AtIndex:和<key>AtIndexes:消息的某種組合项滑,用來創(chuàng)建鍵值編碼對象窗声。如果原始對象還實現(xiàn)了一個名為get<Key>:range:之類的可選方法诈唬,則代理對象也將在適當時使用該方法(注意:方法名的命名規(guī)則要符合KVC的標準命名方法扔亥,包括方法簽名食听。)
如果沒有找到這三個訪問數(shù)組的固翰,請繼續(xù)進入【第三步】
-【第三步】如果沒有找到上面的幾種方法彤委,則會同時查找countOf <Key>剂习,enumeratorOf<Key>和memberOf<Key>這三個方法
如果這三個方法都找到揪垄,則會創(chuàng)建一個響應(yīng)所有NSSet方法的集合代理對象皇拣,并返回該對象严蓖,此代理對象隨后將其收到的所有NSSet消息轉(zhuǎn)換為countOf<Key>,enumeratorOf<Key>和memberOf<Key>:消息的某種組合氧急,用于創(chuàng)建它的對象
如果還是沒有找到颗胡,則進入【第四步】
-【第四步】如果還沒有找到,檢查類方法InstanceVariablesDirectly是否YES态蒂,依次搜索_<key>杭措,_is<Key>,<key>或is<Key>的實例變量
如果搜到钾恢,直接獲取實例變量的值手素,進入【第五步】
-【第五步】根據(jù)搜索到的屬性值的類型,返回不同的結(jié)果
如果是對象指針瘩蚪,則直接返回結(jié)果
如果是NSNumber支持的標量類型泉懦,則將其存儲在NSNumber實例中并返回它
如果是是NSNumber不支持的標量類型,請轉(zhuǎn)換為NSValue對象并返回該對象
-【第六步】如果上面5步的方法均失敗疹瘦,系統(tǒng)會執(zhí)行該對象的valueForUndefinedKey:方法崩哩,默認拋出NSUndefinedKeyException類型的異常
KVC 使用場景
1、動態(tài)設(shè)值和取值
常用的可以通過setValue:forKey: 和 valueForKey:
也可以通過路由的方式setValue:forKeyPath: 和 valueForKeyPath:
2、通過KVC訪問和修改私有變量
在日常開發(fā)中邓嘹,對于類的私有屬性酣栈,在外部定義的對象,是無法直接訪問私有屬性的汹押,但是對于KVC而言矿筝,一個對象沒有自己的隱私,所以可以通過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)部屬性
在日常開發(fā)中,我們知道妙痹,很多UI控件都是在其內(nèi)部由多個UI空間組合而成铸史,這些內(nèi)部控件蘋果并沒有提供訪問的API,但是使用KVC可以解決這個問題怯伊,常用的就是自定義tabbar琳轿、個性化UITextField中的placeHolderText
5、用KVC實現(xiàn)高階消息傳遞
在對容器類使用KVC時震贵,valueForKey:將會被傳遞給容器中的每一個對象利赋,而不是對容器本身進行操作水评,結(jié)果會被添加到返回的容器中猩系,這樣,可以很方便的操作集合 來返回 另一個集合
//KVC實現(xiàn)高階消息傳遞
- (void)transmitMsg{
NSArray *arrStr = @[@"english", @"franch", @"chinese"];
NSArray *arrCapStr = [arrStr valueForKey:@"capitalizedString"];
for (NSString *str in arrCapStr) {
NSLog(@"%@", str);
}
NSArray *arrCapStrLength = [arrCapStr valueForKeyPath:@"capitalizedString.length"];
for (NSNumber *length in arrCapStrLength) {
NSLog(@"%ld", (long)length.integerValue);
}
}
//********打印結(jié)果********
2020-10-27 11:33:43.377672+0800 CJLCustom[60035:6380757] English
2020-10-27 11:33:43.377773+0800 CJLCustom[60035:6380757] Franch
2020-10-27 11:33:43.377860+0800 CJLCustom[60035:6380757] Chinese
2020-10-27 11:33:43.378233+0800 CJLCustom[60035:6380757] 7
2020-10-27 11:33:43.378327+0800 CJLCustom[60035:6380757] 6
2020-10-27 11:33:43.378417+0800 CJLCustom[60035:6380757] 7