iOS-KVC 底層原理

先上一波KVC的官方文檔谒所,

setValue:forKey:的搜索流程:

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é)果如下

image.png

證明官方文檔中確實少寫了這個方法勾怒。嘿嘿婆排,被逮住了吧声旺。
setValue流程圖

接下來看看valueForKey:

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類型的異常


valueForKey流程圖

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

參考:http://www.reibang.com/p/ab8c754761bf

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末中燥,一起剝皮案震驚了整個濱河市寇甸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌疗涉,老刑警劉巖拿霉,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異咱扣,居然都是意外死亡绽淘,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進店門闹伪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沪铭,“玉大人,你說我怎么就攤上這事偏瓤∩钡。” “怎么了?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵厅克,是天一觀的道長赔退。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么硕旗? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任窗骑,我火速辦了婚禮,結(jié)果婚禮上漆枚,老公的妹妹穿的比我還像新娘慧域。我一直安慰自己,他們只是感情好浪读,可當我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布昔榴。 她就那樣靜靜地躺著,像睡著了一般碘橘。 火紅的嫁衣襯著肌膚如雪互订。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天痘拆,我揣著相機與錄音仰禽,去河邊找鬼。 笑死纺蛆,一個胖子當著我的面吹牛吐葵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播桥氏,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼温峭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了字支?” 一聲冷哼從身側(cè)響起凤藏,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎堕伪,沒想到半個月后揖庄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡欠雌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年蹄梢,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片富俄。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡禁炒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蛙酪,到底是詐尸還是另有隱情齐苛,我是刑警寧澤,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布桂塞,位于F島的核電站凹蜂,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜玛痊,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一汰瘫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧擂煞,春花似錦混弥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蒿涎,卻和暖如春哀托,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背劳秋。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工仓手, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人玻淑。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓嗽冒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親补履。 傳聞我的和親對象是個殘疾皇子添坊,可洞房花燭夜當晚...
    茶點故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容