編碼篇-精析OC史詩級技術(shù)之KVC

概述

KVC 全稱 key valued coding 鍵值編碼。

不得不承認(rèn)KVC在開發(fā)過程中是神器一般的存在芳肌。如果正確靈活使用kvc候衍,會使得整個(gè)開發(fā)過程輕松很多。簡單而強(qiáng)大芽唇。

反射機(jī)制是在運(yùn)行狀態(tài)中,對于任意一個(gè)類取劫,都能夠知道這個(gè)類的所有屬性和方法匆笤;對于任意一個(gè)對象,都能夠調(diào)用它的任意一個(gè)方法和屬性.JAVA,C#都有這個(gè)機(jī)制谱邪。ObjC也有炮捧,所以你根部不必進(jìn)行任何操作就可以進(jìn)行屬性的動(dòng)態(tài)讀寫,就是KVC惦银。

KVC的操作方法由NSKeyValueCoding提供咆课,而他是NSObject的類別,也就是說ObjC中幾乎所有的對象都支持KVC操作扯俱。它提供一種機(jī)制來間接訪問對象的屬性书蚪。直接訪問對象是通過調(diào)用訪問器的方法實(shí)現(xiàn),而KVC不需要調(diào)用訪問器的設(shè)置和獲取方法迅栅。

KVC的主要方法和用途

- (nullable id)valueForKey:(NSString *)key;                          //直接通過Key來取值
- (void)setValue:(nullable id)value forKey:(NSString *)key;          //通過Key來設(shè)值
- (nullable id)valueForKeyPath:(NSString *)keyPath;                  //通過KeyPath來取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;  //通過KeyPath來設(shè)值

  ************************************************************************

  當(dāng)然NSKeyValueCoding類別中還有其他的一些方法殊校,下面列舉一些

  + (BOOL)accessInstanceVariablesDirectly;
  //默認(rèn)返回YES,表示如果沒有找到Set<Key>方法的話读存,會按照_key为流,_iskey,key让簿,iskey的順序搜索成員敬察,設(shè)置成NO就不這樣搜索

  - (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
  //KVC提供屬性值正確性?驗(yàn)證的API,它可以用來檢查set的值是否正確尔当、為不正確的值做一個(gè)替換值或者拒絕設(shè)置新值并返回錯(cuò)誤原因莲祸。

  - (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
  //這是集合操作的API,里面還有一系列這樣的API居凶,如果屬性是一個(gè)NSMutableArray虫给,那么可以用這個(gè)方法來返回藤抡。

  - (nullable id)valueForUndefinedKey:(NSString *)key;
  //如果Key不存在侠碧,且沒有KVC無法搜索到任何和Key有關(guān)的字段或者屬性,則會調(diào)用這個(gè)方法缠黍,默認(rèn)是拋出異常弄兜。

  - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
  //和上一個(gè)方法一樣,但這個(gè)方法是設(shè)值。

  - (void)setNilValueForKey:(NSString *)key;
  //如果你在SetValue方法時(shí)面給Value傳nil替饿,則會調(diào)用這個(gè)方法

  - (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
  //輸入一組key,返回該組key對應(yīng)的Value语泽,再轉(zhuǎn)成字典返回,用于將Model轉(zhuǎn)到字典视卢。


setValue:forKey:方法:給模型的屬性賦值

 賦值原理:(以 setIcon為例:)

(1)去模型中查找有沒有setIcon方法,就直接調(diào)用這個(gè)set方法,給模型這個(gè)屬性賦值[self setIcon:dict[@"icon"]];
(2)如果找不到set方法踱卵,接著就會去尋找有沒有icon屬性,如果有,就直接訪問模型中icon = dict[@"icon"];
(3)如果找不到icon屬性,接著又會去尋找_icon屬性,如果有,直接_icon = dict[@"icon"];
(4)如果都找不到就會報(bào)錯(cuò)
    [<Flag 0x7fb74bc7a2c0> setValue:forUndefinedKey:]
  • 直接賦值
  • 支持鍵值路徑
  • 支持操作符
  • 字典轉(zhuǎn)模型
  • 修改UI私有屬性

直接賦值

對于屬性值我們可以通過setter 和getter方法据过,或讀取或?qū)懭霐?shù)值惋砂。
當(dāng)然我們也可以用KVC 的方式進(jìn)行讀寫數(shù)據(jù)。

舉個(gè)例子:

  @interface Person : NSObject
  @property(nonatomic,copy,readonly)NSString* name;
  @property(nonatomic,assign)NSNumber *age;
  @end

 Person *person=[[Person alloc] init];

[person setValue:@"25" forKey:@"age"];
[person setValue:@"皮拉夫大王" forKey:@"name"];

NSLog(@"person 的名字是%@",person.name);
NSLog(@"person 的年領(lǐng)是%@",[person valueForKey:@"age"]);

從上面的例子中我們可以發(fā)現(xiàn):

  • 只讀的屬性怎么可以賦值绳锅?
  • 還有age屬性明明是NSNumber類型的西饵,怎么可以把字符串賦給它?

(1)KVC 不但能夠賦值鳞芙,而且還能破壞只讀的特性眷柔。
(2)更重要的是KVC 有自動(dòng)裝箱(自動(dòng)類型轉(zhuǎn)換)的功能,我們不需要去轉(zhuǎn)換類型了原朝。由于開發(fā)過程中數(shù)據(jù)領(lǐng)域是字符串的天下驯嘱,所以這個(gè)自動(dòng)裝箱的功能的確是極好的。
(3)KVC可以訪問成員變量喳坠,無論是否提供getter/setter方法宙拉,無論可見性是怎樣,是否有readonly修飾丙笋。

支持鍵值路徑

什么叫支持鍵值路徑谢澈?說白了就是支持多層級屬性直接賦值。假如現(xiàn)在有一個(gè)書籍類御板,類中包含了書籍的名稱name锥忿。書籍可以被Person所擁有(就是可以作為person的屬性)

#import <Foundation/Foundation.h>
  @interface Book : NSObject
  @property(nonatomic,copy)NSString* name;
  @end

那么我們就可以這樣來用

Person *person=[[Person alloc] init];
Book *myBook=[[Book alloc] init];
person.book=myBook;

[person setValue:@"程序員攤煎餅指南" forKeyPath:@"book.name"];

NSLog(@"%@",[person valueForKeyPath:@"book.name"]);

需要說明的是:在不必要的情況下使用keyPath會浪費(fèi)性能。

支持操作符

格式為:[p valueForKeyPath:@"Left keypath部分.@Collectionoperator部分.Right keypath部分”];

Left keypath部分:需要操作對象路徑怠肋。
Collectionoperator部分:通過@符號確定使用的集合操作敬鬓。
Right keypath部分:需要進(jìn)行集合操作的屬性。

(1)簡單集合操作符

  @count: 返回一個(gè)值為集合中對象總數(shù)的NSNumber對象笙各。
  @sum: 首先把集合中的每個(gè)對象都轉(zhuǎn)換為double類型钉答,然后計(jì)算其總,最后返回一個(gè)值為這個(gè)總和的NSNumber對象杈抢。
  @avg: 把集合中的每個(gè)對象都轉(zhuǎn)換為double類型数尿,返回一個(gè)值為平均值的NSNumber對象。
  @max: 使用compare:方法來確定最大值惶楼。所以為了讓其正常工作右蹦,集合中所有的對象都必須支持和另一個(gè)對象的比較诊杆。
  @min: 和@max一樣,但是返回的是集合中的最小值何陆。

  [products valueForKeyPath:@"@count"]; 
  [products valueForKeyPath:@"@sum.price"]; 
  [products valueForKeyPath:@"@avg.price"]; 
  [products valueForKeyPath:@"@max.price"]; 
  [products valueForKeyPath:@"@min.launchedOn"]; 

如果操作對象(集合/數(shù)組)內(nèi)是NSNumber晨汹,可以這樣寫

 [products valueForKeyPath:@"@sum.self"]; 

1.使用類的方法做操作符

 NSArray *array = @[@"name", @"w", @"aa", @"jimsa"];
 NSLog(@"%@", [array    valueForKeyPath:@"uppercaseString"]);

 輸出:
(  NAME, W,  AA, JIMSA)

相當(dāng)于數(shù)組中的每個(gè)成員執(zhí)行了uppercaseString方法,然后把返回的對象組成一個(gè)新數(shù)組返回贷盲。既然可以用uppercaseString方法淘这,那么NSString的其他方法也是可以的.

2.剔除重復(fù)數(shù)據(jù)

NSArray *array = @[@"name", @"w", @"aa", @"jimsa", @"aa"]; NSLog(@"%@",     
[array valueForKeyPath:@"@distinctUnionOfObjects.self"]);

打印:
 ( name, w,  jimsa, aa    )

3.對NSDictionary數(shù)組快速找出相應(yīng)key對的值

NSArray *array = @[
@{@"name1" : @"cookeee",@"code" : @1}, 
@{@"name": @"jim",@"code" : @2}, 
@{@"name": @"jim",@"code" : @1},
@{@"name": @"jbos",@"code" : @1}];
NSLog(@"%@", [array valueForKeyPath:@"name"]);

直接得到字典中name key對應(yīng)的值組成的數(shù)組巩剖,顯然比循環(huán)取值再加入到新數(shù)組中方便快捷慨灭,
由于第一個(gè)元素沒有name這個(gè)Key ,所以里面為<null>)

(  "<null>",   jim, jim,   jbos   )

(2)對象操作符

@unionOfObjects:返回操作對象內(nèi)部的所有對象球及,返回值為數(shù)組
@distinctUnionOfObjects:返回操作對象內(nèi)部的不同對象氧骤,返回值為數(shù)組

(3)數(shù)組和集合操作符

@unionOfArrays:返回操作對象(且操作對象內(nèi)對象必須是數(shù)組/集合)中數(shù)組/集合的所有對象,返回值為數(shù)組
@distinctUnionOfArrays:返回操作對象(且操作對象內(nèi)對象必須是數(shù)組/集合)中數(shù)組/集合的不同對象吃引,返回值為數(shù)組
@distinctUnionOfSets:返回操作對象(且操作對象內(nèi)對象必須是數(shù)組/集合)中數(shù)組/集合的所有對象筹陵,返回值為集合

提示:集合無重復(fù)元素

(4)自定義操作符

NSArray為例,runtime跑一下

#import <objc/runtime.h>
unsigned int outconunt = 0;
Method *meths =class_copyMethodList([NSArray class], &outconunt);
for (int i = 0; i<outconunt; i++) {
    Method meth = meths[i];
    SEL metSel = method_getName(meth);
    NSLog(@"L : %@",NSStringFromSelector(metSel));
    
}

可以看到一大堆的方法镊尺,由于太多了朦佩,無法截圖完整的,看上圖紅框中的代碼是不是很眼熟庐氮。

猜想:實(shí)現(xiàn)_<key>ForKeyPath:即可自定義Collection Operators
嘗試定義一個(gè)名為@jackCollection Operators

可見语稠,只要寫好實(shí)現(xiàn),完全可以自定義一些比較有用的Collection Operators

字典轉(zhuǎn)模型

下面是常見的使用方法弄砍,目前有很多KVC 和 Runtime一起使用達(dá)到Json數(shù)據(jù)自動(dòng)轉(zhuǎn)模型的方法仙畦,本文暫時(shí)不做介紹。

@implementation Model
-(instancetype)initWithDict:(NSDictionary *)dict
{
    if (self=[super init])
    {
        // 字典轉(zhuǎn)模型的常用語句
        [self setValuesForKeysWithDictionary:dict];
    }
    return self;
}

-(void)setValue:(id)value forUndefinedKey:(NSString *)key
{
    if ([key isEqualToString:@"id"])
    {
          self.whoCare=value;
      }
  }
@end

修改UI私有屬性

(1)如何實(shí)現(xiàn)這樣的效果音婶?

系統(tǒng)默認(rèn)的是這樣的:


看了系統(tǒng)自帶的API慨畸,無法解決這個(gè)問題,現(xiàn)在有兩個(gè)路:

  • 自定義PageControl
  • 通過runtime遍歷出UIPageControl所有屬性(包括私有成員屬性)利用KVC可強(qiáng)制修改系統(tǒng)的PageControl衣式,達(dá)到想要的效果寸士。充滿了黑科技之感
 u_int count;
  Ivar *properties =class_copyIvarList([UIPageControl class], &count);
  for (int i = 0; i<count; i++)
  {
    const char* propertyName =ivar_getName(properties[i]);
    const char* propertyType = ivar_getTypeEncoding(properties[i]);
    NSLog(@"屬性:%@  =  %@",[NSString stringWithUTF8String: propertyName],[NSString stringWithUTF8String: propertyType]);
  }

結(jié)果非常滿意,果然找到我想要的圖片設(shè)置屬性碴卧。
然后通過KVC設(shè)置自定義圖片弱卡,實(shí)現(xiàn)了效果,代碼如下:

 UIPageControl *pageControl = [[UIPageControl alloc] init]; 
 [pageControl setValue:[UIImage imageNamed:@"home_slipt_nor"] forKeyPath:@"_pageImage"];
 [pageControl setValue:[UIImage imageNamed:@"home_slipt_pre"] forKeyPath:@"_currentPageImage"];

(2)還有很多其他的修改UI控件私有屬性的常見操作:
比如修改UISearchBar的輸入框顯示效果住册。

 UITextField * searchField = [searchBar valueForKey:@"searchField"];
 [searchField setValue:GrayTextColor forKeyPath:@"placeholderLabel.textColor"];
 [searchField setValue:[UIFont boldSystemFontOfSize:10] forKeyPath:@"_placeholderLabel.font"];


 UITextField *searchField = [[mySearchBar subviews] lastObject];
 [searchField setReturnKeyType:UIReturnKeyDone];

參考文章:

iOS開發(fā)技巧系列---詳解KVC
KVC進(jìn)階(三)
iOS底層-KVC使用實(shí)踐以及實(shí)現(xiàn)原理
iOS開發(fā)技巧系列---詳解KVC(我告訴你KVC的一切)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末婶博,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子界弧,更是在濱河造成了極大的恐慌凡蜻,老刑警劉巖搭综,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件垢箕,死亡現(xiàn)場離奇詭異划栓,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)条获,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進(jìn)店門忠荞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人帅掘,你說我怎么就攤上這事委煤。” “怎么了修档?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵碧绞,是天一觀的道長。 經(jīng)常有香客問我吱窝,道長讥邻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任院峡,我火速辦了婚禮兴使,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘照激。我一直安慰自己发魄,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布俩垃。 她就那樣靜靜地躺著励幼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪口柳。 梳的紋絲不亂的頭發(fā)上赏淌,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天,我揣著相機(jī)與錄音啄清,去河邊找鬼六水。 笑死,一個(gè)胖子當(dāng)著我的面吹牛辣卒,可吹牛的內(nèi)容都是我干的掷贾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼荣茫,長吁一口氣:“原來是場噩夢啊……” “哼想帅!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起啡莉,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤港准,失蹤者是張志新(化名)和其女友劉穎旨剥,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浅缸,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡轨帜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了衩椒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚌父。...
    茶點(diǎn)故事閱讀 40,115評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖毛萌,靈堂內(nèi)的尸體忽然破棺而出苟弛,到底是詐尸還是另有隱情,我是刑警寧澤阁将,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布膏秫,位于F島的核電站,受9級特大地震影響做盅,放射性物質(zhì)發(fā)生泄漏缤削。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一言蛇、第九天 我趴在偏房一處隱蔽的房頂上張望僻他。 院中可真熱鬧,春花似錦腊尚、人聲如沸吨拗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽劝篷。三九已至,卻和暖如春民宿,著一層夾襖步出監(jiān)牢的瞬間娇妓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工活鹰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哈恰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓志群,卻偏偏與公主長得像着绷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子锌云,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評論 2 355

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

  • KVC(Key-value coding)鍵值編碼荠医,單看這個(gè)名字可能不太好理解。其實(shí)翻譯一下就很簡單了,就是指iO...
    朽木自雕也閱讀 1,560評論 6 1
  • KVC(Key-value coding)鍵值編碼彬向,單看這個(gè)名字可能不太好理解兼贡。其實(shí)翻譯一下就很簡單了,就是指iO...
    黑暗中的孤影閱讀 49,740評論 74 441
  • 一直以來,微博熱搜這個(gè)設(shè)置都會在一段時(shí)間引導(dǎo)輿論導(dǎo)向缕棵,甚至?xí)蔀楹芏嗳瞬栌囡埡蟮恼勝Y孵班。 最近一段時(shí)間涉兽,何炅連續(xù)上了...
    筱陌_閱讀 490評論 0 0
  • 有你們的時(shí)光是最美的風(fēng)景招驴,原來遇見你們才是我最大的幸運(yùn),高中是人生最美的時(shí)光枷畏,很幸運(yùn)能在這里和你們相遇别厘,相識,相...
    jiancc閱讀 265評論 0 0
  • 上上期《演員的誕生》拥诡,因?yàn)橛H自下場撕節(jié)目組触趴,袁立成了爭議的焦點(diǎn)。 以至于同期其他演員都無人關(guān)注渴肉。 上期節(jié)目冗懦,大家又...
    Sir電影閱讀 17,885評論 61 529