iOS底層原理22:KVC底層原理

KVC的全稱是Key-Value Coding间狂,即鍵值編碼帽借,是一種由NSKeyValueCoding非正式協(xié)議啟用的機(jī)制峻村,對(duì)象采用它來提供對(duì)其屬性的間接訪問。當(dāng)一個(gè)對(duì)象符合鍵值編碼時(shí)忧勿,它的屬性可以通過一個(gè)簡(jiǎn)潔、統(tǒng)一的消息傳遞接口通過字符串參數(shù)來尋址瞻讽。這種間接訪問機(jī)制補(bǔ)充了實(shí)例變量及其關(guān)聯(lián)的訪問方法所提供的直接訪問鸳吸。

KVC相關(guān)API

常用方法

  • 通過key設(shè)值/取值
//直接通過Key來取值
- (nullable id)valueForKey:(NSString *)key;

//通過Key來設(shè)值
- (void)setValue:(nullable id)value forKey:(NSString *)key;
  • 通過keyPath(路由)設(shè)值/取值
//通過KeyPath來取值
- (nullable id)valueForKeyPath:(NSString *)keyPath; 

//通過KeyPath來設(shè)值                 
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;  

其他方法

//默認(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;

KVC設(shè)值的底層原理

在日常開發(fā)中漓穿,針對(duì)對(duì)象屬性的賦值,一般有以下兩種方式

  • 直接通過setter方法賦值
  • 通過KVC鍵值編碼的相關(guān)API賦值
HTPerson *p = [[HTPerson alloc] init];
p.name = @"name";
[p setValue:@"kvcName" forKey:@"name"];

??下面針對(duì)使用最多的KVC設(shè)值方法:setValue:forKey注盈,來進(jìn)行其底層原理的探索晃危。

  • 首先進(jìn)入setValue:forKey的聲明,發(fā)現(xiàn)是在Foundation框架中,而Foundation框架是不開源的
image
image

當(dāng)調(diào)用setValue:forKey:設(shè)置屬性value時(shí)震叮,其底層的執(zhí)行流程為

image

  • 【第一步】首先查找是否有這三種setter方法,按照查找順序?yàn)?code>set<Key>:--> _set<Key>: --> setIs<Key>:
    • 如果有其中任意一個(gè)setter方法鳍鸵,則直接設(shè)置屬性的value(注意:key是指成員變量名苇瓣,首字符大小寫需要符合KVC的命名規(guī)范)
    • 如果都沒有,則進(jìn)入【第二步】
  • 【第二步】:如果沒有第一步中的三個(gè)簡(jiǎn)單的setter方法偿乖,則查找accessInstanceVariablesDirectly返回值击罪,返回NO,則進(jìn)入【第三步】
    • 如果返回YES贪薪,則查找間接訪問的實(shí)例變量進(jìn)行賦值媳禁,查找順序?yàn)椋?code>_<key> --> _is<Key> -> <key> -> is<Key>
    • 如果找到其中任意一個(gè)實(shí)例變量,則賦值
    • 如果都沒有画切,則進(jìn)入【第三步】
  • 【第三步】如果setter方法 或者 實(shí)例變量都沒有找到竣稽,系統(tǒng)會(huì)執(zhí)行該對(duì)象的setValue:forUndefinedKey:方法,默認(rèn)拋出NSUndefinedKeyException類型的異常

KVC取值底層原理

查看蘋果文檔Key-Value Coding Programming Guide槽唾,當(dāng)調(diào)用valueForKey:時(shí)丧枪,其底層的執(zhí)行流程為

image

  • 【第一步】首先查找getter方法光涂,按照get<Key> --> <key> --> is<Key> --> _<key>的方法順序查找
    • 如果找到了庞萍,就調(diào)用它,并使用結(jié)果繼續(xù)【第五步】
    • 如果沒有找到忘闻,則進(jìn)入【第二步】
  • 【第二步】如果第一步中的getter方法沒有找到钝计,KVC會(huì)查找countOf<Key>objectIn<Key>AtIndex:<key>AtIndexes:
    • 如果找到countOf<Key>和其他兩個(gè)中的一個(gè),則會(huì)創(chuàng)建一個(gè)響應(yīng)所有NSArray方法的集合代理對(duì)象齐佳,響應(yīng)所有NSArray方法返回該對(duì)象私恬,即NSKeyValueArray,是NSArray子類炼吴。代理對(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í)使用該方法(注意:方法名的命名規(guī)則要符合KVC的標(biāo)準(zhǔn)命名方法,包括方法簽名童芹。)
    • 如果沒有找到這三個(gè)訪問數(shù)組的涮瞻,請(qǐng)繼續(xù)進(jìn)入【第三步】
  • 【第三步】如果沒有找到上面的幾種方法,則會(huì)同時(shí)查找countOf<Key>假褪,enumeratorOf<Key>memberOf<Key>:這三個(gè)方法
    • 如果這三個(gè)方法都找到,則會(huì)創(chuàng)建一個(gè)響應(yīng)所有NSSet方法的集合代理對(duì)象宁否,并返回該對(duì)象,此代理對(duì)象隨后將其收到的所有NSSet消息轉(zhuǎn)換為countOf<Key>enumeratorOf<Key>memberOf<Key>:消息的某種組合督怜,用于創(chuàng)建它的對(duì)象
    • 如果還是沒有找到丰歌,則進(jìn)入【第四步】
  • 【第四步】如果還沒有找到立帖,檢查類方法InstanceVariablesDirectly是否YES,依次搜索_<key>绰筛,_is<Key>窿克,<key>is<Key>的實(shí)例變量
    • 如果搜到敞恋,直接獲取實(shí)例變量的值,進(jìn)入【第五步】
    • 如果類方法InstanceVariablesDirectly返回NO,進(jìn)入【第六步】
  • 【第五步】根據(jù)搜索到的屬性值的類型,返回不同的結(jié)果
    • 如果是對(duì)象指針,則直接返回結(jié)果
    • 如果是NSNumber支持的標(biāo)量類型矮锈,則將其存儲(chǔ)在NSNumber實(shí)例中并返回它
    • 如果是NSNumber不支持的標(biāo)量類型子眶,請(qǐng)轉(zhuǎn)換為NSValue對(duì)象并返回該對(duì)象
  • 【第六步】如果上面5步的方法均失敗,系統(tǒng)會(huì)執(zhí)行該對(duì)象的valueForUndefinedKey:方法,默認(rèn)拋出NSUndefinedKeyException類型的異常

使用路由訪問蝌麸,即keyPath

在日常開發(fā)中弟疆,一個(gè)類的成員變量有可能是自定義類或者其他的復(fù)雜數(shù)據(jù)類型同廉,一般的操作是蟆湖,我們可以先通過KVC獲取該屬性,然后再通過KVC獲取自定義類的屬性,就是比較麻煩晦鞋,還有另一種比較簡(jiǎn)便的方法,就是使用KeyPath路由棺克,涉及以下兩個(gè)方法:setValue:forKeyPath:valueForKeyPath:

//通過KeyPath來取值
- (nullable id)valueForKeyPath:(NSString *)keyPath;                  

//通過KeyPath來設(shè)值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;  

KVC使用場(chǎng)景

1悠垛、動(dòng)態(tài)設(shè)值和取值

  • 常用的可以通過setValue:forKey:valueForKey:
  • 也可以通過路由的方式setValue:forKeyPath:valueForKeyPath:

2、通過KVC訪問和修改私有變量

在日常開發(fā)中娜谊,對(duì)于類的私有屬性确买,在外部定義的對(duì)象,是無法直接訪問私有屬性的纱皆,但是對(duì)于KVC而言湾趾,一個(gè)對(duì)象沒有自己的隱私,所以可以通過KVC修改和訪問任何私有屬性

3派草、多值操作(model和字典互轉(zhuǎn))

model和字典的轉(zhuǎn)換可以通過下面兩個(gè)KVC的API實(shí)現(xià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)部由多個(gè)UI控件組合而成艺普,這些內(nèi)部控件蘋果并沒有提供訪問的API,但是使用KVC可以解決這個(gè)問題鉴竭,常用的就是自定義tabbar歧譬、個(gè)性化UITextField中的placeHolderText

5、用KVC實(shí)現(xiàn)高階消息傳遞

在對(duì)容器類使用KVC時(shí)搏存,valueForKey:將會(huì)被傳遞給容器中的每一個(gè)對(duì)象瑰步,而不是對(duì)容器本身進(jìn)行操作,結(jié)果會(huì)被添加到返回的容器中璧眠,這樣缩焦,可以很方便的操作集合 來返回 另一個(gè)集合

//KVC實(shí)現(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);
    }
}
image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末读虏,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子舌界,更是在濱河造成了極大的恐慌掘譬,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呻拌,死亡現(xiàn)場(chǎng)離奇詭異葱轩,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)藐握,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門靴拱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人猾普,你說我怎么就攤上這事袜炕。” “怎么了初家?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵偎窘,是天一觀的道長。 經(jīng)常有香客問我溜在,道長陌知,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任掖肋,我火速辦了婚禮仆葡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘志笼。我一直安慰自己沿盅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布纫溃。 她就那樣靜靜地躺著腰涧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪皇耗。 梳的紋絲不亂的頭發(fā)上南窗,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音郎楼,去河邊找鬼。 笑死窒悔,一個(gè)胖子當(dāng)著我的面吹牛呜袁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播简珠,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼阶界,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼虹钮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起膘融,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤芙粱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后氧映,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體春畔,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年岛都,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了律姨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡臼疫,死狀恐怖择份,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情烫堤,我是刑警寧澤荣赶,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站鸽斟,受9級(jí)特大地震影響拔创,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜湾盗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一伏蚊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧格粪,春花似錦躏吊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至疆导,卻和暖如春赁项,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背澈段。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工悠菜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人败富。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓悔醋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親兽叮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子芬骄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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

  • iOS 底層原理 文章匯總[http://www.reibang.com/p/412b20d9a0f6] KVC...
    Style_月月閱讀 3,499評(píng)論 0 20
  • KVC的全稱是Key-Value Coding猾愿,翻譯成中文是 鍵值編碼,鍵值編碼是由NSKeyValueCodin...
    AcmenL閱讀 469評(píng)論 0 3
  • KVC是什么? KVC的全稱是Key-Value Coding账阻,翻譯成中文是 鍵值編碼蒂秘,鍵值編碼是由NSKeyVa...
    含笑州閱讀 381評(píng)論 0 1
  • KVC的全稱是Key-Value Coding,翻譯成中文是 鍵值編碼淘太,鍵值編碼是由NSKeyValueCodin...
    輝輝歲月閱讀 1,009評(píng)論 0 4
  • 16宿命:用概率思維提高你的勝算 以前的我是風(fēng)險(xiǎn)厭惡者姻僧,不喜歡去冒險(xiǎn),但是人生放棄了冒險(xiǎn)琴儿,也就放棄了無數(shù)的可能段化。 ...
    yichen大刀閱讀 6,052評(píng)論 0 4