KVC鍵值編碼 & KVO鍵值監(jiān)聽

KVC簡(jiǎn)介

我們知道可以通過setter惦积、getter方法來設(shè)置和修改對(duì)象的屬性接校,也知道如何通過簡(jiǎn)化的點(diǎn)語法來設(shè)置、修改對(duì)象的屬性狮崩。實(shí)際上蛛勉,Objective-C還支持一種更靈活的操作方式,這種方式允許以字符串形式間接操作對(duì)象的屬性睦柴,這種方式的全稱是Key Value Coding(簡(jiǎn)稱KVC)诽凌,即鍵值編碼。

簡(jiǎn)單的KVC

最基本的KVC由NSKeyValueCoding協(xié)議提供支持坦敌,最基本的操作屬性的兩個(gè)方法如下:

setValue:屬性值forKey:屬性名:為指定屬性設(shè)置值侣诵。

valueForKey:屬性名:獲取指定屬性的值。

用法如下:

@property (nonatomic, copy) NSString *name;

設(shè)定:

[object setValue:@"Suxiaoyao" forKey:@"name"]

取值:

NSString *nameStr = [object valueForKey:@"name"]

對(duì)于setValue:屬性值 forKey:@"name"狱窘;代碼杜顺,底層的執(zhí)行機(jī)制如下:

(1).程序優(yōu)先調(diào)用“setName:屬性值;”代碼通過setter方法完成設(shè)置。

(2).如果該類沒有setName:方法训柴,KVC機(jī)制會(huì)搜索該類名為_name的成員變量哑舒,找到后對(duì)_name成員變量賦值。

(3).如果該類既沒有setName:方法幻馁,也沒有定義_name成員變量洗鸵,KVC機(jī)制會(huì)搜索該類名為name的成員變量越锈,找到后對(duì)name成員變量賦值。

(4).如果上面3條都沒有找到膘滨,系統(tǒng)將會(huì)執(zhí)行該對(duì)象的setValue: forUndefinedKey:方法甘凭。默認(rèn)setValue: forUndefinedKey:方法會(huì)引發(fā)一個(gè)異常,將會(huì)導(dǎo)致程序崩潰火邓。

對(duì)于“valueForKey:@"name";”代碼丹弱,底層執(zhí)行機(jī)制如下:

(1).程序優(yōu)先調(diào)用"name;"代碼來獲取該getter方法的返回值。

(2).如果該類沒有name方法铲咨,KVC機(jī)制會(huì)搜索該類名為_name的成員變量躲胳,找到后返回_name成員變量的值。

(3).如果該類既沒有name方法纤勒,也沒有定義_name成員變量坯苹,KVC機(jī)制會(huì)搜索該類名為name的成員變量,找到后返回name成員變量的值摇天。

(4).如果上面3條都沒有找到粹湃,系統(tǒng)將會(huì)執(zhí)行該對(duì)象的valueForUndefinedKey:方法。默認(rèn)valueForUndefinedKey:方法會(huì)引發(fā)一個(gè)異常泉坐,將會(huì)導(dǎo)致程序崩潰为鳄。

處理不存在的key

前面提到過,當(dāng)使用KVC方式操作屬性時(shí)腕让,這些屬性可能不存在孤钦,此時(shí)系統(tǒng)只是引發(fā)了異常,并沒有進(jìn)行任何特別的處理记某。但是在我們的實(shí)際開發(fā)中司训,結(jié)合我們的業(yè)務(wù)場(chǎng)景,我們總是不希望程序會(huì)崩潰液南,此時(shí)我們可以考慮重寫setValue: forUndefinedKey:方法與valueForUndefinedKey:方法。

- (void)setValue:(id)value forUndefinedKey:(NSString *)key {? ?

NSLog(@"您設(shè)置的key:[%@]不存在", key);? ?

NSLog(@"您設(shè)置的value為:[%@]", value);

}

- (id)valueForUndefinedKey:(NSString *)key {?

? NSLog(@"您訪問的key:[%@]不存在", key);

returnnil;

}

這樣的話勾徽,當(dāng)KVC操作并不存在的key時(shí)滑凉,KVC機(jī)制總是會(huì)調(diào)用重寫的方法進(jìn)行處理,通過這種處理機(jī)制喘帚,可以非常方便的定制自己的處理行為畅姊。

處理nil值

當(dāng)調(diào)用KVC來設(shè)置對(duì)象的屬性時(shí),如果屬性的類型是對(duì)象類型(如NSString)吹由,嘗試將屬性設(shè)置為nil若未,是合法的,程序可以正常運(yùn)行割岛。

但是如果屬性的類型是基本類型(如int振愿、float、double)腰池,嘗試將屬性設(shè)置為nil隙疚,程序?qū)?huì)崩潰引發(fā)以下異常'NSInvalidArgumentException',并且從提示信息可以知道setNilValueForKey:方法導(dǎo)致了這個(gè)異常壤追。當(dāng)程序嘗試為某個(gè)屬性設(shè)置nil值時(shí),如果該屬性并不接受nil值供屉,那么程序?qū)?huì)自動(dòng)執(zhí)行該對(duì)象的setNilValueForKey:方法行冰。我們同樣可以重寫這個(gè)方法:

- (void)setNilValueForKey:(NSString *)key {

//對(duì)不能接受nil的屬性進(jìn)行處理

if([key isEqualToString:@"price"]) {

//對(duì)應(yīng)你具體的業(yè)務(wù)來處理price =0;? ?

}else{? ? ?

? [supersetNilValueForKey:key];?

? }

}

我們可以通過重寫這個(gè)方法,并且根據(jù)我們不同的業(yè)務(wù)場(chǎng)景做單獨(dú)處理伶丐。

Key路徑(Key Path)

KVC 同樣允許我們通過關(guān)系來訪問對(duì)象悼做。假設(shè) person 對(duì)象有屬性 address,address 有屬性 city哗魂,我們可以這樣通過 person 來訪問 city:

[person valueForKeyPath:@"address.city"];

值得注意的是這里我們調(diào)用 -valueForKeyPath: 而不是 -valueForKey:贿堰。

KVC協(xié)議中為操作Key路徑的方法如下:

setValue:forKeyPath: 根據(jù)Key路徑設(shè)置屬性值

valueForKeyPath: 根據(jù)Key路徑獲取屬性值

集合的操作

一個(gè)常常被忽視的 KVC 特性是它對(duì)集合操作的支持。舉個(gè)例子啡彬,我們可以這樣來獲得一個(gè)數(shù)組中最大的值:

NSArray *a = @[@4, @84, @2];

NSLog(@"max = %@", [a valueForKeyPath:@"@max.self"]);

或者說羹与,我們有一個(gè) Transaction 對(duì)象的數(shù)組,對(duì)象有屬性 amount 的話庶灿,我們可以這樣獲得最大的 amount:

NSArray *a = @[transaction1, transaction2, transaction3];

NSLog(@"max = %@", [a valueForKeyPath:@"@max.amount"]);

當(dāng)我們調(diào)用[a valueForKeyPath:@"@max.amount"]的時(shí)候纵搁,它會(huì)在數(shù)組 a的每個(gè)元素中調(diào)用 -valueForKey:@"amount"然后返回最大的那個(gè)。KVC 的蘋果官方文檔有一個(gè)章節(jié)Collection Operators詳細(xì)的講述了類似的用法往踢。

KVC小結(jié)

前面介紹了這么多內(nèi)容腾誉,大家可能感到疑惑,為什么要用KVC方式來操作呢峻呕?直接調(diào)用對(duì)象的setter與getter方法進(jìn)行操作不可以嗎利职?是不是KVC方式的性能更好呢?實(shí)際上瘦癌,通過KVC操作對(duì)象的性能比通過setter猪贪、getter方式操作的性能更差,使用KVC編程的優(yōu)勢(shì)是更加簡(jiǎn)潔讯私,更適合提煉一些通用性質(zhì)的代碼热押。由于KVC允許通過字符串形式來操作對(duì)象的屬性,這個(gè)字符串既可是常量斤寇,也可是變量桶癣,因此具有極高的靈活性。

鍵值監(jiān)聽(KVO)

在iOS應(yīng)用的開發(fā)過程中娘锁,iOS應(yīng)用通常會(huì)把應(yīng)用程序組件分開成數(shù)據(jù)模型組件和視圖組件牙寞,其中數(shù)據(jù)模型組件負(fù)責(zé)維護(hù)應(yīng)用程序的狀態(tài)數(shù)據(jù),而視圖組件則負(fù)責(zé)顯示數(shù)據(jù)模型組件內(nèi)部的狀態(tài)數(shù)據(jù)莫秆。

對(duì)于上面的設(shè)計(jì)結(jié)構(gòu)间雀,如果程序存在的需求是:在數(shù)據(jù)模型組件的狀態(tài)數(shù)據(jù)發(fā)生改變時(shí)悔详,視圖組件能動(dòng)態(tài)地更新自己,及時(shí)顯示數(shù)據(jù)模型組件更新后的數(shù)據(jù)雷蹂。

iOS為我們提供了一種優(yōu)秀的解決方案:利用KVO(Key Value Observing)機(jī)制伟端。

KVO機(jī)制NSKeyValueObserving協(xié)議提供支持,當(dāng)然匪煌,NSObject遵守了該協(xié)議责蝠,因此,NSObject的子類都可使用該協(xié)議中的方法萎庭,該協(xié)議包含如下常用的方法可用于注冊(cè)監(jiān)聽器:

addObserver:forKeyPath:options:context: 注冊(cè)一個(gè)監(jiān)聽器用于監(jiān)聽指定Key路徑

removeObserver:forKeyPath: 為指定Key路徑刪除指定的監(jiān)聽器

removeObserver:forKeyPath:context: 為指定Key路徑刪除指定的監(jiān)聽器霜医,只是多了一個(gè)context參數(shù)。

對(duì)于上面的需求驳规,很容易想到可以讓視圖組件來監(jiān)聽數(shù)據(jù)模型組件的改變肴敛,當(dāng)數(shù)據(jù)模型組件的key路徑對(duì)應(yīng)的屬性發(fā)生改變時(shí),作為監(jiān)聽器的視圖組件將被激發(fā)吗购,激發(fā)時(shí)就會(huì)回調(diào)監(jiān)聽器自身的監(jiān)聽方法医男,該監(jiān)聽方法如下:

observeValueForKeyPath:ofObject:change:context:

由此可見,作為監(jiān)聽器的視圖組件需要重寫observeValueForKeyPath:ofObject:change:context:方法捻勉,重寫該方法時(shí)就可以得到最新修改的數(shù)據(jù)镀梭,從而使用最新的數(shù)據(jù)來更新視圖組件的顯示。

KVO編程的步驟如下:

為被監(jiān)聽對(duì)象(通常是數(shù)據(jù)模型組件)注冊(cè)監(jiān)聽器

重寫監(jiān)聽器的observeValueForKeyPath:ofObject:change:context:方法

移除監(jiān)聽器

KVO實(shí)例場(chǎng)景

我們要監(jiān)聽一個(gè)人的心跳踱启,并且在屏幕上顯示出來

我們定義出model

@interface Person : NSObject/**

心跳

*/@property (nonatomic, copy) NSString *heartbeat;

@end

@implementation Person

@end

定義此model為Controller的屬性报账,實(shí)例化它,監(jiān)聽它的屬性埠偿,并顯示在當(dāng)前的View里邊

@interface ViewController ()

@property (nonatomic, strong) Person *person;

@property (nonatomic, strong) UILabel *heartbeatLabel;

@end

@implementation ViewController

- (void)viewDidLoad {?

? [superviewDidLoad];

// Do any additional setup after loading the view, typically from a nib.self.person = [[Person alloc] init];?

[self.person setValue:@"72"forKey:@"heartbeat"];? ?

[self.person addObserver:self forKeyPath:@"heartbeat"options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];?

self.heartbeatLabel = [[UILabel alloc] initWithFrame:CGRectMake(100,100,100,30)];

self.heartbeatLabel.textColor = [UIColor redColor]; ?

self.heartbeatLabel.text = [self.person valueForKey:@"heartbeat"];? ?

[self.view addSubview:self.heartbeatLabel];? ?

UIButton *runButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];? ?

runButton.frame = CGRectMake(0,0,100,30);? ?

runButton.backgroundColor = [UIColor redColor];? ?

[runButton addTarget:self action:@selector(run:) forControlEvents:UIControlEventTouchUpInside];? ?

[self.view addSubview:runButton];

}

當(dāng)點(diǎn)擊button的時(shí)候透罢,調(diào)用run方法,修改對(duì)象的屬性

- (void)run:(UIButton *)sender {? ?

[self.person setValue:@"100"forKey:@"heartbeat"];

}

實(shí)現(xiàn)回調(diào)方法

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context{

if([keyPath isEqualToString:@"heartbeat"])? ? {? ? ? ?

self.heartbeatLabel.text = [change objectForKey:@"new"];? ?

}

}

增加觀察與取消觀察是成對(duì)出現(xiàn)的冠蒋,所以需要在最后的時(shí)候羽圃,移除觀察者

- (void)dealloc {? ? [self.person removeObserver:self forKeyPath:@"heartbeat"];}

KVO小結(jié)

KVO這種編碼方式使用起來很簡(jiǎn)單,很適用于model修改后浊服,引發(fā)的view的變化這種情況统屈,就像上邊的例子那樣,當(dāng)更改屬性的值后牙躺,監(jiān)聽對(duì)象會(huì)立即得到通知。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末腕扶,一起剝皮案震驚了整個(gè)濱河市孽拷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌半抱,老刑警劉巖脓恕,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件膜宋,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡炼幔,警方通過查閱死者的電腦和手機(jī)秋茫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來乃秀,“玉大人肛著,你說我怎么就攤上這事《逖叮” “怎么了枢贿?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)刀脏。 經(jīng)常有香客問我局荚,道長(zhǎng),這世上最難降的妖魔是什么愈污? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任耀态,我火速辦了婚禮,結(jié)果婚禮上暂雹,老公的妹妹穿的比我還像新娘首装。我一直安慰自己,他們只是感情好擎析,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布簿盅。 她就那樣靜靜地躺著,像睡著了一般揍魂。 火紅的嫁衣襯著肌膚如雪桨醋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天现斋,我揣著相機(jī)與錄音喜最,去河邊找鬼。 笑死庄蹋,一個(gè)胖子當(dāng)著我的面吹牛瞬内,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播限书,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼虫蝶,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了倦西?” 一聲冷哼從身側(cè)響起能真,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后粉铐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疼约,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年蝙泼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了程剥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡汤踏,死狀恐怖织鲸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情茎活,我是刑警寧澤昙沦,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站载荔,受9級(jí)特大地震影響盾饮,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜懒熙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一丘损、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧工扎,春花似錦徘钥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至橱健,卻和暖如春而钞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拘荡。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工臼节, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人珊皿。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓网缝,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親蟋定。 傳聞我的和親對(duì)象是個(gè)殘疾皇子粉臊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

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

  • 作者:wangzz原文地址:http://blog.csdn.net/wzzvictory/article/det...
    反調(diào)唱唱閱讀 1,118評(píng)論 0 5
  • 在iOS開發(fā)中维费,我們常常用到鍵值編碼KVC和鍵值監(jiān)聽KVO兩個(gè)東東果元,今天小編和大家分享的就是這兩個(gè)東東在應(yīng)用開發(fā)中...
    突然自我閱讀 1,001評(píng)論 2 3
  • 在iOS開發(fā)過程中促王,我們經(jīng)常會(huì)聽到或者用到KVO犀盟,KVC,NSNotificationCenter等蝇狼,但是很多時(shí)候...
    dullgrass閱讀 7,077評(píng)論 14 133
  • kvc簡(jiǎn)述 kvc即鍵值編碼阅畴,在iOS中的應(yīng)用主要體現(xiàn)在開發(fā)者通過key訪問對(duì)象的屬性或給對(duì)象的屬性賦值。這樣做最...
    RunnerFL閱讀 489評(píng)論 0 0
  • 可能是我總是太古怪了吧迅耘。所以才會(huì)在自己知道贱枣,你知道我喜歡你的時(shí)候選擇死不承認(rèn)。好不容易承認(rèn)后颤专,發(fā)現(xiàn)你身邊已經(jīng)有了其...
    陽秦閱讀 432評(píng)論 0 0