KVC

今天和大家討論一下OC中KVC(KeyValueCoding)鍵值編碼

KVC定義

KVC(KeyValueCoding)鍵值編碼技術(shù)可以讓我們?cè)贠C的開發(fā)中使用字符串作為Key來訪問某個(gè)對(duì)象的屬性值或者為某個(gè)對(duì)象的屬性值進(jìn)行賦值。而不需要通過暴露在頭文件中的存取器方法進(jìn)行訪問和賦值操作狸吞。通過這種方法勉耀,我們可以獲取某個(gè)對(duì)象并沒有暴露在頭文件中的實(shí)例變量和為這個(gè)實(shí)例變量進(jìn)行賦值操作。通過這種方法可以繞過編譯器的審查蹋偏,使我們能夠做出一些”不太合規(guī)“的操作便斥,而不會(huì)讓編譯器報(bào)錯(cuò)。因此這種形式的存取是在運(yùn)行時(shí)進(jìn)行的威始,也是OC動(dòng)態(tài)特性的一種體現(xiàn)枢纠。

KVC原理

NSObject有個(gè)分類(NSKeyValueCoding)關(guān)于KVC的實(shí)現(xiàn)都在里面,所以黎棠,所有繼承自NSObject的對(duì)象都可以使用KVC晋渺,而結(jié)構(gòu)體和某些Swift對(duì)象
因?yàn)闆]有繼承NSObject而不可以使用

KVC取值設(shè)值以及查找順序

KVC進(jìn)行取值和設(shè)值操作時(shí)會(huì)根據(jù)你所傳遞的字符串按照一定規(guī)律進(jìn)行查找,我們編寫代碼進(jìn)行測(cè)試脓斩,以找到這個(gè)規(guī)律木西。

首先我們新建一個(gè)Person類

  • 取值

在我們程序入口利用KVC來獲取Person示例的name屬性

Person *zhangSan = [Person new];
    NSLog(@"%@",[zhangSan valueForKey:@"name"]);

在Person.m編寫代碼如下:


#import "Person.h"

@interface Person ()
{
    NSString *_name;//第一個(gè)查找的實(shí)例變量
    NSString *_isName;//第二個(gè)查找的實(shí)例變量
    NSString *name;//第三個(gè)查找的實(shí)例變量
    NSString *isName;//第四個(gè)查找的實(shí)例變量
    
}

@end

@implementation Person

- (instancetype)init
{
    self = [super init];
    if (self) {
        
        _name = @"ivar _name";
        _isName = @"ivar _isName";
        name = @"ivar name";
        isName = @"ivar isName";
        
    }
    return self;
}

//第一個(gè)取值查找的方法
-(NSString *)getName
{
    return @"getName method";
}
//第二個(gè)取值查找的方法
-(NSString *)name
{
    
    return @"name method";
}
//第三個(gè)取值查找的方法
-(NSString *)isName
{
    return @"isName method";
}
//第四個(gè)取值查找的方法
-(NSString *)_name
{
    return @"_name method";
}

//當(dāng)成數(shù)組處理
-(NSInteger)countOfName
{
    return 2;
}

-(id)objectInNameAtIndex:(NSInteger)index
{
    return @"數(shù)組成員";
}
//是否可以直接操作實(shí)例變量,默認(rèn)返回yes
+(BOOL)accessInstanceVariablesDirectly
{
    NSLog(@"accessInstanceVariablesDirectly");
    return YES;
}

@end

經(jīng)過代碼測(cè)試我們發(fā)現(xiàn)俭厚,當(dāng)我們對(duì)Person的示例使用KVC來獲取它的name屬性時(shí)户魏,系統(tǒng)先會(huì)按照順序查找一系列方法是否存在如果存在便調(diào)用并拿到這個(gè)方法的返回值作為kvc取值的結(jié)果,這一系列的方法的順序便是我上文代碼中注釋的順序挪挤,分別是getName name isName _name,如果第一個(gè)方法就存在那便不再調(diào)用之后的方法叼丑,以此類推。

當(dāng)四個(gè)方法全都沒有找到時(shí)扛门,我們的系統(tǒng)會(huì)把name當(dāng)做數(shù)組去處理鸠信,看看是否實(shí)現(xiàn)了代碼中的兩個(gè)方法。如果實(shí)現(xiàn)了论寨,就返回一個(gè)數(shù)組星立。

如果數(shù)組方法也沒有找到爽茴,會(huì)當(dāng)做集合去處理,在實(shí)現(xiàn)中查找相應(yīng)的方法绰垂。

如果集合的相關(guān)方法也沒有找到室奏,系統(tǒng)會(huì)調(diào)用accessInstanceVariablesDirectly方法秽之,這個(gè)方法是指示是否允許直接操作示例變量的泽本,如果你沒有重寫這個(gè)方法,默認(rèn)返回是yes吟逝。如果返回NO則程序不再往下執(zhí)行占业,崩潰绒怨,如果返回yes,那么將會(huì)按照順序查找相應(yīng)的示例變量谦疾,他們的順序是:_name _isName name isName;如果有對(duì)應(yīng)的實(shí)例變量那就返回他的值南蹂,如果四個(gè)都查找不到,那么程序崩潰念恍。

  • 賦值

賦值的方式和取值的方式類似也是先按順序查找方法六剥,如果方法都沒有查找到,那么調(diào)用accessInstanceVariablesDirectly方法峰伙,如果返回yes則會(huì)按照同樣的順序查找相應(yīng)的實(shí)例變量

在程序入口處:

    Person *zhangSan = [Person new];
   [zhangSan setValue:@"2341" forKey:@"name"];

在Person.m編寫代碼如下:

//第一個(gè)查找的方法
-(void)setName:(NSString *)name
{
    NSLog(@"setName %@",name);
}
//第二個(gè)查找的方法
-(void)_setName:(NSString *)name
{
    NSLog(@"_setName %@",name);
}

keypath

在KVC中還可以使用keypath路徑來訪問屬性
舉例說明:就剛剛的Person類我們給它創(chuàng)建一個(gè)lover屬性仗考,lover的類型也是Person那么我們想要獲取某個(gè)Person對(duì)象的lover的name我們要怎么做呢?我們可以這樣

    Person *zhangSan = [Person new];
   [zhangSan valueForKeyPath:@"lover.name"];

KVC允許我們使用.來連接屬性這樣我們可以方便的獲取對(duì)象的屬性的屬性词爬。秃嗜。。

避免崩潰

正常情況下如果我們把nil設(shè)置給一個(gè)非對(duì)象屬性顿膨,或者查找一個(gè)并不存在的key程序就會(huì)崩潰锅锨,我們通過重寫下面的這些方法可以避免崩潰

-(void)setNilValueForKey:(NSString *)key
{
    NSLog(@"不允許設(shè)置為nil");
}

-(void)setValue:(id)value forUndefinedKey:(NSString *)key
{
    NSLog(@"不存在這個(gè)key");
}

-(id)valueForUndefinedKey:(NSString *)key
{
    NSLog(@"不存在這個(gè)key");
    return nil;
}

數(shù)字和結(jié)構(gòu)體

當(dāng)屬性類型為數(shù)字或者結(jié)構(gòu)體時(shí),我們不能直接用數(shù)字和結(jié)構(gòu)體來賦值恋沃,而是需要轉(zhuǎn)化為NSNumber和NSValue來賦值必搞,同樣的,我們獲取到的值也會(huì)被轉(zhuǎn)化為NSNumber和NSValue類型囊咏。

數(shù)組的KVC應(yīng)用

在數(shù)組中KVC有一些特有的運(yùn)算符恕洲,我們一起看一下用法吧
我們編寫代碼如下:

     Person *zhangsan = [Person new];
    [zhangsan setValue:@10 forKey:@"age"];
    
    Person *lisi = [Person new];
    [lisi setValue:@20 forKey:@"age"];
    
    Person *wangwu = [Person new];
    [wangwu setValue:@30 forKey:@"age"];
    
    Person *zhaoliu = [Person new];
    [zhaoliu setValue:@10 forKey:@"age"];
    
    NSArray *peoples = @[zhangsan,lisi,wangwu,zhaoliu];

    NSLog(@"%ld",((NSNumber*)[peoples valueForKeyPath:@"@min.age"]).integerValue) ;
    NSLog(@"%ld",((NSNumber*)[peoples valueForKeyPath:@"@max.age"]).integerValue) ;
    NSLog(@"%ld",((NSNumber*)[peoples valueForKeyPath:@"@sum.age"]).integerValue) ;
    NSLog(@"%ld",((NSNumber*)[peoples valueForKeyPath:@"@avg.age"]).integerValue) ;
    NSLog(@"%ld",((NSNumber*)[peoples valueForKeyPath:@"@count"]).integerValue) ;
    NSLog(@"%@",[peoples valueForKeyPath:@"@distinctUnionOfObjects.age"]);
    NSLog(@"%@",[peoples valueForKeyPath:@"@unionOfObjects.age"]);  

輸出結(jié)果為

2018-09-29 16:57:49.628811+0800 KvcKvoTest[254:3290335] 10
2018-09-29 16:57:49.628903+0800 KvcKvoTest[254:3290335] 30
2018-09-29 16:57:49.629141+0800 KvcKvoTest[254:3290335] 70
2018-09-29 16:57:49.629283+0800 KvcKvoTest[254:3290335] 17
2018-09-29 16:57:49.629370+0800 KvcKvoTest[254:3290335] 4
2018-09-29 16:57:49.629520+0800 KvcKvoTest[254:3290335] (
    10,
    20,
    30
)
2018-09-29 16:57:49.629647+0800 KvcKvoTest[254:3290335] (
    10,
    20,
    30,
    10
)

前面的五種運(yùn)算符相信大家都可以明白它的用法,后面的兩個(gè)運(yùn)算符distinctUnionOfObjects為將數(shù)組中的對(duì)應(yīng)屬性排序去重返回一個(gè)數(shù)組梅割,unionOfObjects為將數(shù)組中的對(duì)應(yīng)屬性返回一個(gè)數(shù)組霜第。

字典的KVC應(yīng)用

字典也可以像普通對(duì)象一樣使用kvc,因此valueForKeyPath可以很方便的用來操作多層字典户辞。

還有兩個(gè)方法用來字典轉(zhuǎn)對(duì)象和對(duì)象轉(zhuǎn)字典:

  • 對(duì)象轉(zhuǎn)字典:
     Person *zhangsan = [Person new];
    [zhangsan setValue:@10 forKey:@"age"];
    [zhangsan setValue:@"zhangsan" forKey:@"name"];
    
    NSDictionary *dic= [zhangsan dictionaryWithValuesForKeys:@[@"age",@"name"]];
    NSLog(@"%@",dic);
  • 字典轉(zhuǎn)對(duì)象:
Person *zhangsan = [Person new];
    [zhangsan setValuesForKeysWithDictionary:@{@"name":@"zhangsan",@"age":@10}];

KVC用途總結(jié)

使用KVC可以動(dòng)態(tài)的取值泌类,賦值,可以操作私有變量底燎,可以修改一些系統(tǒng)控件刃榨,可以對(duì)數(shù)組進(jìn)行一些操作弹砚,以及對(duì)象和字典的轉(zhuǎn)換。
和所有OC的動(dòng)態(tài)特性一樣枢希,如果濫用會(huì)容易導(dǎo)致崩潰和一些不好排查的問題出現(xiàn)桌吃。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市苞轿,隨后出現(xiàn)的幾起案子读存,更是在濱河造成了極大的恐慌,老刑警劉巖呕屎,帶你破解...
    沈念sama閱讀 211,561評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異敬察,居然都是意外死亡秀睛,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門莲祸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蹂安,“玉大人,你說我怎么就攤上這事锐帜√镉” “怎么了?”我有些...
    開封第一講書人閱讀 157,162評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵缴阎,是天一觀的道長允瞧。 經(jīng)常有香客問我,道長蛮拔,這世上最難降的妖魔是什么述暂? 我笑而不...
    開封第一講書人閱讀 56,470評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮建炫,結(jié)果婚禮上畦韭,老公的妹妹穿的比我還像新娘。我一直安慰自己肛跌,他們只是感情好艺配,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著衍慎,像睡著了一般转唉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上稳捆,一...
    開封第一講書人閱讀 49,806評(píng)論 1 290
  • 那天酝掩,我揣著相機(jī)與錄音,去河邊找鬼眷柔。 笑死期虾,一個(gè)胖子當(dāng)著我的面吹牛原朝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播镶苞,決...
    沈念sama閱讀 38,951評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼喳坠,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了茂蚓?” 一聲冷哼從身側(cè)響起壕鹉,我...
    開封第一講書人閱讀 37,712評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎聋涨,沒想到半個(gè)月后晾浴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,166評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡牍白,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評(píng)論 2 327
  • 正文 我和宋清朗相戀三年脊凰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茂腥。...
    茶點(diǎn)故事閱讀 38,643評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡狸涌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出最岗,到底是詐尸還是另有隱情帕胆,我是刑警寧澤,帶...
    沈念sama閱讀 34,306評(píng)論 4 330
  • 正文 年R本政府宣布般渡,位于F島的核電站懒豹,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏驯用。R本人自食惡果不足惜歼捐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望晨汹。 院中可真熱鬧豹储,春花似錦、人聲如沸淘这。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽铝穷。三九已至钠怯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間曙聂,已是汗流浹背晦炊。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人断国。 一個(gè)月前我還...
    沈念sama閱讀 46,351評(píng)論 2 360
  • 正文 我出身青樓贤姆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親稳衬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子霞捡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評(píng)論 2 348

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

  • 元旦的時(shí)候,我給Z君發(fā)消息說薄疚,17歲的我認(rèn)識(shí)你的第18年碧信,元旦快樂。Z君很快回復(fù)了我街夭,別提了砰碴,都是孽緣啊。...
    遺忘的罐罐閱讀 250評(píng)論 0 2
  • 下午必須去實(shí)驗(yàn)小學(xué)講課板丽,開學(xué)第一講《拒絕誘惑 快樂成長》呈枉;就是這么巧,也是這個(gè)時(shí)間段組長發(fā)布消息檐什,必須聽課、評(píng)課弱卡,...
    知心玲姐閱讀 100評(píng)論 0 1
  • 農(nóng)歷丁酉年九月廿八婶博,星期四瓮具,立冬第十天。 早上4:30鬧鈴起床凡人,含貞爸蹲著聽早課讀經(jīng)典:《易經(jīng)》上經(jīng)泰卦~大有卦名党;...
    育心經(jīng)典包志剛閱讀 362評(píng)論 0 4