OC底層原理16-KVC底層原理

iOS--OC底層原理文章匯總

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
最后編輯于
?著作權(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
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)愿卸。 經(jīng)常有香客問我灵临,道長(zhǎ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
  • 文/蒼蘭香墨 我猛地睜開眼惧财,長(zhǎng)吁一口氣:“原來是場(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ú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有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
  • 我被黑心中介騙來泰國(guó)打工斟薇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人恕酸。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓堪滨,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親尸疆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子椿猎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355