KVC 的原理概述

一、KVC 很簡(jiǎn)單

KVC 很簡(jiǎn)單可都,每個(gè)人都會(huì)用,僅有的 API 如下:
1蚓耽、setValue: forKeyPath:
2汹粤、setValue: forKey:
3、valueForKeyPath:
4田晚、valueForKey:

前兩個(gè)是設(shè)置值(value),后兩個(gè)是通過(guò) key 或者 keyPath 獲取對(duì)應(yīng)的 value(值)国葬。

在接下來(lái)的介紹中贤徒,大家主要關(guān)注非常規(guī)用法。

二汇四、KVC 的實(shí)質(zhì)

通常所理解的 KVC 是:設(shè)置或者獲取某個(gè)屬性或者成員變量的值接奈。但是問(wèn)題來(lái)了,如果沒(méi)有對(duì)應(yīng)的屬性獲取成員變量通孽,又會(huì)怎樣呢序宦?接下來(lái)分批介紹一下。

2.1 常規(guī)用法

有一個(gè)屬性是這樣定義的:

// 名字
@property (nonatomic, copy) NSString* name;

我們可以直接通過(guò)屬性名使用 KVC:

KVCObject* kvcObj = [[KVCObject alloc] init];

// 通過(guò)屬性名設(shè)置具體的 value
[kvcObj setValue:@"CoderHG" forKey:@"name"];
// 通過(guò)屬性獲取具體的 value
NSString* name = [kvcObj valueForKey:@"name"];
NSLog(@"姓名: %@", name);

以上的設(shè)置與獲取 value 都會(huì)執(zhí)行其屬性的 setter 與 getter 方法背苦。

通過(guò)成員變量使用 KVC:

KVCObject* kvcObj = [[KVCObject alloc] init];

// 通過(guò)成員變量設(shè)置具體的 value
[kvcObj setValue:@"CoderHG" forKey:@"_name"];
// 通過(guò)成員變量獲取具體的 value
NSString* name = [kvcObj valueForKey:@"_name"];
NSLog(@"姓名: %@", name);

以上的設(shè)置與獲取 value 都 會(huì)執(zhí)行其屬性的 setter 與 getter 方法互捌。

是的、在開發(fā)中只需要知道這兩種常規(guī)的用法就足夠了行剂。但是對(duì)于程序員來(lái)說(shuō)除了開發(fā)秕噪、還有一個(gè)名詞叫 面試,如果面試的時(shí)候只知道上面的用法厚宰,那肯定是不夠的腌巾。

2.2 特別的 key 需要特別的處理

這里的 特別的意思是遂填,這個(gè) key 不是對(duì)應(yīng)的屬性,也并非對(duì)應(yīng)的成員變量澈蝙,也可以稱為 非常規(guī)吓坚。那么這種情況,KVC 又是如何處理的呢灯荧?看一下如下代碼:

KVCObject* kvcObj = [[KVCObject alloc] init];
// 非常規(guī)設(shè)置具體的 value
[kvcObj setValue:@"CoderHG" forKey:@"goddess"];
// 非常規(guī)獲取具體的 value
NSString* name = [kvcObj valueForKey:@"goddess"];
NSLog(@"姓名: %@", name);

其中 goddess 既不是屬性礁击、也不似成員變量。這種情況漏麦,如果不做任何的處理客税,直接運(yùn)行代碼,那肯定會(huì) crash 的撕贞。

setValue: forKey: 的 crash 日志:

*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: 
'[<KVCObject 0x60800001eaa0> setValue:forUndefinedKey:]: 
this class is not key value coding-compliant for the key goddess.'

valueForKey: 的 crash 日志:

*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: 
'[<KVCObject 0x608000004c30> valueForUndefinedKey:]: 
this class is not key value coding-compliant for the key goddess.'

這種情況在 KVC 中就直接沒(méi)救了么更耻?不是的,其實(shí)在 crash 之前做了很多的查找工作的捏膨。

setValue: forKey: 的查詢順序是這樣的:

  • 1秧均、依次查找是否實(shí)現(xiàn)了這些對(duì)象方法:setKey:、_setKey:号涯。一旦找到其中的一個(gè)實(shí)現(xiàn)目胡,則直接調(diào)用,都沒(méi)有找到链快,則進(jìn)入下一步查找誉己。
  • 2、第一步未找到域蜗,調(diào)用 +accessInstanceVariablesDirectly 方法巨双,如果返回為 NO,則直接 crash霉祸,返回為 YES筑累,則會(huì)進(jìn)入下一步。這個(gè)方法默認(rèn)返回 YES丝蹭。
  • 3慢宗、第二步返回 YES, 則會(huì)繼續(xù)查找是否有如下的成員變量:_key、_isKey奔穿、key 與 isKey镜沽。找到則直接賦值,沒(méi)有找到則直接 crash贱田。

注意:第一步查找的是 方法淘邻,第三部查找的是 成員變量

valueForKey: 的查詢順序是這樣的:

  • 1湘换、按照順序依次查找這些對(duì)象方法:getKey宾舅、key统阿、isKey 與 _key。找到則執(zhí)行筹我,沒(méi)有找到則進(jìn)入下一步扶平。
  • 2、調(diào)用 +accessInstanceVariablesDirectly 方法蔬蕊,如果返回為 NO结澄,則直接 crash,返回為 YES岸夯,則會(huì)進(jìn)入下一步麻献。這個(gè)方法默認(rèn)返回 YES。
    3猜扮、第二步返回 YES, 則會(huì)繼續(xù)查找是否有如下的成員變量:_key勉吻、_isKey、key 與 isKey旅赢。找到則獲取對(duì)應(yīng)成員變量的值齿桃,沒(méi)有找到則直接 crash。

溫馨提示: 關(guān)于第2煮盼、3步短纵,兩種情況是類似的。

總結(jié)

其實(shí)寫本簡(jiǎn)書的重點(diǎn)是關(guān)注 非常規(guī)的情況僵控,主要是記住其查找步驟即可香到。大概就是先查找方法、在查找成員變量报破。

在寫本簡(jiǎn)書的時(shí)候养渴,我有一個(gè)試驗(yàn) Demo # OC2Nature,可以作為一個(gè)參考泛烙。具體請(qǐng)看 KVC 目錄。

謝謝翘紊!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蔽氨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子帆疟,更是在濱河造成了極大的恐慌鹉究,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件踪宠,死亡現(xiàn)場(chǎng)離奇詭異自赔,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)柳琢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門绍妨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)润脸,“玉大人,你說(shuō)我怎么就攤上這事他去”醒保” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵灾测,是天一觀的道長(zhǎng)爆价。 經(jīng)常有香客問(wèn)我,道長(zhǎng)媳搪,這世上最難降的妖魔是什么铭段? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮秦爆,結(jié)果婚禮上序愚,老公的妹妹穿的比我還像新娘。我一直安慰自己鲜结,他們只是感情好展运,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著精刷,像睡著了一般拗胜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上怒允,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天埂软,我揣著相機(jī)與錄音,去河邊找鬼纫事。 笑死勘畔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的丽惶。 我是一名探鬼主播炫七,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼钾唬!你這毒婦竟也來(lái)了万哪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤抡秆,失蹤者是張志新(化名)和其女友劉穎奕巍,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體儒士,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡的止,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了着撩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诅福。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡匾委,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出权谁,到底是詐尸還是另有隱情剩檀,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布旺芽,位于F島的核電站沪猴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏采章。R本人自食惡果不足惜运嗜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望悯舟。 院中可真熱鬧担租,春花似錦、人聲如沸抵怎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)反惕。三九已至尝艘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間姿染,已是汗流浹背背亥。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留悬赏,地道東北人狡汉。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像闽颇,于是被迫代替她去往敵國(guó)和親盾戴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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