KVO機(jī)制詳解

Key-Value Observing機(jī)制

知識點(diǎn)介紹

Key-Value Observing (簡寫為KVO):當(dāng)指定的對象的屬性被修改了,允許對象接受到通知的機(jī)制厉亏。每次指定的被觀察對象的屬性被修改的時(shí)候,KVO都會自動的去通知相應(yīng)的觀察者抗悍。

KVO的優(yōu)點(diǎn):

當(dāng) 有屬性改變宜猜,KVO會提供自動的消息通知。這樣的架構(gòu)有很多好處秫舌。首先,開發(fā)人員不需要自己去實(shí)現(xiàn)這樣的方案:每次屬性改變了就發(fā)送消息通知绣檬。這是KVO 機(jī)制提供的最大的優(yōu)點(diǎn)足陨。因?yàn)檫@個(gè)方案已經(jīng)被明確定義,獲得框架級支持娇未,可以方便地采用墨缘。開發(fā)人員不需要添加任何代碼,不需要設(shè)計(jì)自己的觀察者模型,直接可 以在工程里使用镊讼。其次宽涌,KVO的架構(gòu)非常的強(qiáng)大,可以很容易的支持多個(gè)觀察者觀察同一個(gè)屬性蝶棋,以及相關(guān)的值卸亮。

KVO如何工作:

需要三個(gè)步驟來建立一個(gè)屬性的觀察員。理解這三個(gè)步驟就可以知道KVO如何設(shè)計(jì)工作的玩裙。 (1)首先兼贸,構(gòu)思一下如下實(shí)現(xiàn)KVO是否有必要。比如吃溅,一個(gè)對象溶诞,當(dāng)另一個(gè)對象的特定屬性改變的時(shí)候,需要被通知到决侈。

例 如螺垢,PersonObject希望能夠覺察到BankObject對象的accountBalance屬性的任何變化。 (2)那么 PersonObject必須發(fā)送一個(gè)“addObserver:forKeyPath:options:context:”消息颜及,注冊成為 BankObject的accountBalance屬性的觀察者甩苛。(說 明:“addObserver:forKeyPath:options:context:”方法在指定對象實(shí)例之間建立了一個(gè)連接蹂楣。注意俏站,這個(gè)連接不是兩 個(gè)類之間建立的,而是兩個(gè)對象實(shí)例之間建立的痊土。) (3)為了能夠響應(yīng)消息肄扎,觀察者必須實(shí)現(xiàn) “observeValueForKeyPath:ofObject:change:context:”方法。這個(gè)方法實(shí)現(xiàn)如何響應(yīng)變化的消息赁酝。在這個(gè)方 法里面我們可以跟自己的情況犯祠,去實(shí)現(xiàn)應(yīng)對被觀察對象屬性變動的相應(yīng)邏輯。 (4)假如遵循KVO規(guī)則的話酌呆,當(dāng)被觀察的屬性改變的話衡载,方法 “observeValueForKeyPath:ofObject:change:context:”會自動被調(diào)用。

參考資料

http://www.cocoadev.cn/CocoaDev/Key-Value-Observing-Quick-Start-cn.asp

本知識點(diǎn)在此例中的應(yīng)用

復(fù)制代碼

//注冊監(jiān)聽@implementation RootViewController

- (void)viewDidLoad

{

//監(jiān)聽屬性“earthquakeList”

/* KVO: listen for changes to our earthquake data source for table

view updates*/

[self addObserver:self

forKeyPath:@"earthquakeList" options:0 context:NULL];

}@end
//屬性發(fā)生改變時(shí)

- (void)insertEarthquakes:(NSArray *)earthquakes

{

// this will allow us as an observer to notified

(see observeValueForKeyPath)*/

// so we can update our UITableView

[self willChangeValueForKey:@"earthquakeList"];

[self.earthquakeList addObjectsFromArray:earthquakes];

[self didChangeValueForKey:@"earthquakeList"];

}

//當(dāng)屬性的值發(fā)生變化時(shí)隙袁,自動調(diào)用此方法

/* listen for changes to the earthquake list coming from our app delegate. */

- (void)observeValueForKeyPath:(NSString *)keyPath

ofObject:(id)object

change:(NSDictionary *)change

context:(void *)context

{

[self.tableView reloadData];

}

cocoa的KVO模型中痰娱,有兩種通知觀察者的方式,自動通知和手動通知菩收。顧名思義梨睁,自動通知由cocoa在屬性值變化時(shí)自動通知觀察者,而手動通知需要在值變化時(shí)調(diào)用 willChangeValueForKey:和didChangeValueForKey: 方法通知調(diào)用者娜饵。為求簡便坡贺,我們一般使用自動通知。

要使用手動通知,需要在 automaticallyNotifiesObserversForKey方法中明確告訴cocoa遍坟,哪些鍵值要使用自動通知:

復(fù)制代碼

//重新實(shí)現(xiàn)NSObject類中的automaticallyNotifiesObserversForKey:方法拳亿,返回yes表示自動通知。

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString*)key

{

//當(dāng)這兩個(gè)值改變時(shí)愿伴,使用自動通知已注冊過的觀察者风瘦,觀察者需要實(shí)現(xiàn)observeValueForKeyPath:ofObject:change:context:方法

if ([key isEqualToString:@"isFinished"])

{

return NO;

}

return [super automaticallyNotifiesObserversForKey:key];

}

手動通知在需要改變值_isFinished變量的地方,使用

[self willChangeValueForKey:@"isFinished"];

finished = YES;

[self didChangeValueForKey:@"isFinished"];

自動通知在需要改變_isFinished變量的地方公般,使用

[self setValue:[NSNumber numberWithBool:YES] forKey:@"isFinished"];

方法万搔,而不是僅僅使用簡單賦值。

我們需要在3個(gè)地方改變isFinished值為YES官帘,請求結(jié)束時(shí)瞬雹、連接出錯(cuò)誤,線程被cancel刽虹。請?jiān)趯?yīng)的方法代碼中加入上面的語句酗捌。

最后,需要在觀察者的代碼中進(jìn)行注冊涌哲。打開ViewController中調(diào)用NSOperation子類的地方胖缤,加入:

復(fù)制代碼

//kvo注冊

[operation addObserver:self forKeyPath:@"isFinished"

options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:operation];

并實(shí)現(xiàn) observeValueForKeyPath 方法:

//接收變更通知

- (void)observeValueForKeyPath:(NSString *)keyPath

ofObject:(id)object

change:(NSDictionary *)change

context:(void *)context

{

if ([keyPath isEqual:@"isFinished"]) {

BOOL isFinished=[[change objectForKey:NSKeyValueChangeNewKey] intValue];

if (isFinished) {//如果服務(wù)器數(shù)據(jù)接收完畢

[indicatorView stopAnimating];

URLOperation* ctx=(URLOperation*)context;

NSStringEncoding enc=CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);

NSLog(@"%@",[[NSString alloc] initWithData:[ctx data] encoding:enc]);

//取消kvo注冊

[ctx removeObserver:self

forKeyPath:@"isFinished"];

}

}else{

// be sure to call the super implementation

// if the superclass implements it

[super observeValueForKeyPath:keyPath

ofObject:object

change:change

context:context];

}

}

一,概述

KVO,即:Key-Value Observing阀圾,它提供一種機(jī)制哪廓,當(dāng)指定的對象的屬性被修改后,則對象就會接受到通知初烘。簡單的說就是每次指定的被觀察的對象的屬性被修改后涡真,KVO就會自動通知相應(yīng)的觀察者了。

二肾筐,使用方法

系統(tǒng)框架已經(jīng)支持KVO哆料,所以程序員在使用的時(shí)候非常簡單。

  1. 注冊吗铐,指定被觀察者的屬性东亦,

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

  3. 移除觀察

三,實(shí)例:

假設(shè)一個(gè)場景,股票的價(jià)格顯示在當(dāng)前屏幕上唬渗,當(dāng)股票價(jià)格更改的時(shí)候典阵,實(shí)時(shí)顯示更新其價(jià)格。

1.定義DataModel谣妻,


@interface StockData : NSObject {

NSString * stockName;

float price;

}

@end

@implementation StockData

@end

2.定義此model為Controller的屬性萄喳,實(shí)例化它,監(jiān)聽它的屬性蹋半,并顯示在當(dāng)前的View里邊


- (void)viewDidLoad

{

[super viewDidLoad];

stockForKVO = [[StockData alloc] init];

[stockForKVO setValue:@"searph" forKey:@"stockName"];

[stockForKVO setValue:@"10.0" forKey:@"price"];

[stockForKVO addObserver:self forKeyPath:@"price" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL];

myLabel = [[UILabel alloc]initWithFrame:CGRectMake(100, 100, 100, 30 )];

myLabel.textColor = [UIColor redColor];

myLabel.text = [stockForKVO valueForKey:@"price"];

[self.view addSubview:myLabel];

UIButton * b = [UIButton buttonWithType:UIButtonTypeRoundedRect];

b.frame = CGRectMake(0, 0, 100, 30);

[b addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];

[self.view addSubview:b];

}

3.當(dāng)點(diǎn)擊button的時(shí)候他巨,調(diào)用buttonAction方法,修改對象的屬性

-(void) buttonAction

{

[stockForKVO setValue:@"20.0" forKey:@"price"];

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

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

{

if([keyPath isEqualToString:@"price"])

{

myLabel.text = [stockForKVO valueForKey:@"price"];

}

}

5.增加觀察與取消觀察是成對出現(xiàn)的,所以需要在最后的時(shí)候染突,移除觀察者


- (void)dealloc

{

[super dealloc];

[stockForKVO removeObserver:self forKeyPath:@"price"];

[stockForKVO release];

}

四捻爷,小結(jié)

KVO這種編碼方式使用起來很簡單,很適用與datamodel修改后份企,引發(fā)的UIVIew的變化這種情況也榄,就像上邊的例子那樣,當(dāng)更改屬性的值后司志,監(jiān)聽對象會立即得到通知甜紫。

KVC、KVO即NSKeyValueCoding和NSKeyValueObserving的簡稱骂远。

那我們KVO囚霸、KVC用來做什么的我們又怎么使用它呢?

首先我們先了解下KVO的機(jī)制

KVO:當(dāng)指定的對象的屬性被修改了激才,允許對象接收到通知的機(jī)制拓型。每當(dāng)在類中定義一個(gè)監(jiān)聽

如:

[self addObserver:self forKeyPath:@"items"

options:0 context:contexStr];

當(dāng)然你還可以監(jiān)聽其他對象的屬性變化

[person addObserver:money forKeyPath:@"account"

options:0 context:contexStr];

只要當(dāng)前類中items這個(gè)屬性發(fā)生的變化都會觸發(fā)到以下的方法。


- (void)observeValueForKeyPath:(NSString *)keyPath

ofObject:(id)object

change:(NSDictionary *)change

context:(void *)context

KVO的優(yōu)點(diǎn):

當(dāng)有屬性改變瘸恼,KVO會提供自動的消息通知劣挫。這樣開發(fā)人員不需要自己去實(shí)現(xiàn)這樣的方案:每次屬性改變了就發(fā)送消息通知。

這是KVO機(jī)制提供的最大的優(yōu)點(diǎn)东帅。因?yàn)檫@個(gè)方案已經(jīng)被明確定義压固,獲得框架級支持,可以方便地采用冰啃。

開發(fā)人員不需要添加任何代碼邓夕,不需要設(shè)計(jì)自己的觀察者模型刘莹,直接可以在工程里使用阎毅。

其次,KVO的架構(gòu)非常的強(qiáng)大点弯,可以很容易的支持多個(gè)觀察者觀察同 一個(gè)屬性扇调,以及相關(guān)的值。

KVC的實(shí)現(xiàn)分析

KVC運(yùn)用了一個(gè)isa-swizzling技術(shù)抢肛。

isa-swizzling就是類型混合指針機(jī)制狼钮。KVC主要通過isa-swizzling,來實(shí)現(xiàn)其內(nèi)部查找定位的捡絮。

isa指針,就是is a kind of的意思,指向維護(hù)分發(fā)表的對象的類熬芜。該分發(fā)表實(shí)際上包含了指向?qū)崿F(xiàn)類中的方法的指針,和其它數(shù)據(jù)福稳。

如下KVC的代碼:

[person setValue:@"personName" forKey:@"name"];

就會被編譯器處理成:

SEL sel = sel_get_uid ("setValue:forKey:");

IMP method = objc_msg_lookup (person->isa,sel);

method(person, sel, @"personName", @"name");

其中:

SEL數(shù)據(jù)類型:它是編譯器運(yùn)行Objective-C里的方法的環(huán)境參數(shù)涎拉。

IMP數(shù)據(jù)類型:他其實(shí)就是一個(gè) 編譯器內(nèi)部實(shí)現(xiàn)時(shí)候的函數(shù)指針。當(dāng)Objective-C編譯器去處理實(shí)現(xiàn)一個(gè)方法的時(shí)候,就會指向一個(gè)IMP對象鼓拧,這個(gè)對象是C語言表述的類型半火。


KVC在調(diào)用方法setValue的時(shí)候

(1)首先根據(jù)方法名找到運(yùn)行方法的時(shí)候所需要的環(huán)境參數(shù)。

(2)他會從自己isa指針結(jié)合環(huán)境參數(shù)季俩,找到具體的方法實(shí)現(xiàn)的接口钮糖。

(3)再直接查找得來的具體的方法實(shí)現(xiàn)。

這樣的話前面介紹的KVO實(shí)現(xiàn)就好理解了

當(dāng)一個(gè)對象注冊了一個(gè)觀察者,被觀察對象的isa指針被修改的時(shí)候,isa指針就會指向一個(gè)中間類酌住,而不是真實(shí)的類店归。

所以isa指針其實(shí)不需要指向?qū)嵗龑ο笳鎸?shí)的類。所以我們的程序最好不要依賴于isa指針酪我。在調(diào)用類的方法的時(shí)候娱节,最好要明確對象實(shí)例的類名。

這樣只有當(dāng)我們調(diào)用KVC去訪問key值的時(shí)候KVO才會起作用祭示。所以肯定確定的是肄满,KVO是基于KVC實(shí)現(xiàn)的。

iOS開發(fā)筆記之基于鍵值的觀察者模式(KVO)

KVO簡而言之就是:基于鍵值的觀察者质涛,實(shí)際上就是觀察者模式稠歉。

Cocoa Framework已經(jīng)為我們提供了這一模式,不需要我們自己來實(shí)現(xiàn)了汇陆。我們只需要按照約定的方式去做就可以了怒炸。KVO主要用于用戶界面交互,當(dāng)多個(gè)View共同使用了同一個(gè)實(shí)體毡代,當(dāng)這個(gè)實(shí)體中的某個(gè)屬性改變時(shí)阅羹,如果需要更新多個(gè)界面,KVO的作用就發(fā)揮出來了教寂。

下面給出一個(gè)簡單的示例捏鱼,來展示如何使用KVO。

示例下載

有兩種方式可以在鍵值改變的時(shí)候給觀察者發(fā)送通知:自動方式和手動方式酪耕。其中自動方式是由NSObject提供的一個(gè)默認(rèn)實(shí)現(xiàn)导梆,通常情況下,如果你自定義了一個(gè)類是從NSObject繼承而來迂烁,那么該類就已經(jīng)具有了KVO的自動通知功能看尼,而且不需要額外的編寫代碼。如果需要手動控制通知方式盟步,那么需要重寫automaticallyNotifiesObserversForKey:方法藏斩。在該方法中如果需要手動控制通知方式,則將automaticallyNotifiesObserversForKey:返回NO却盘,否則返回YES狰域。

下面的例子是將openingBalance屬性設(shè)置為手動通知方式窜觉,其他屬性默認(rèn)為自動通知方式


+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {

BOOL automatic = NO;

if ([theKey isEqualToString:@"openingBalance"])

{

automatic = NO;

}

else

{

automatic=[super automaticallyNotifiesObserversForKey:theKey];

}

return automatic;

}

手動通知方式的好處在于可以減少不必要的通知,比如你可以首先檢測一下該屬性值是否發(fā)生改變北专,如果發(fā)生改變則通知禀挫,否則不通知,代碼示例如下:


- (void)setOpeningBalance:(double)theBalance {

if (theBalance != openingBalance) {

[self willChangeValueForKey:@"openingBalance"];

openingBalance=theBalance;

[self didChangeValueForKey:@"openingBalance"];

}

}

如果一個(gè)單一的操作引發(fā)了多個(gè)屬性值的改變拓颓,那么就必須嵌套改變通知语婴。代碼示例如下:


- (void)setOpeningBalance:(double)theBalance {

[self willChangeValueForKey:@"openingBalance"];

[self willChangeValueForKey:@"itemChanged"];

openingBalance=theBalance;

itemChanged=itemChanged+1;

[self didChangeValueForKey:@"itemChanged"];

[self didChangeValueForKey:@"openingBalance"];

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市驶睦,隨后出現(xiàn)的幾起案子砰左,更是在濱河造成了極大的恐慌,老刑警劉巖场航,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缠导,死亡現(xiàn)場離奇詭異,居然都是意外死亡溉痢,警方通過查閱死者的電腦和手機(jī)僻造,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來孩饼,“玉大人髓削,你說我怎么就攤上這事《迫ⅲ” “怎么了立膛?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長梯码。 經(jīng)常有香客問我宝泵,道長,這世上最難降的妖魔是什么轩娶? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任儿奶,我火速辦了婚禮,結(jié)果婚禮上罢坝,老公的妹妹穿的比我還像新娘廓握。我一直安慰自己,他們只是感情好嘁酿,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著男应,像睡著了一般闹司。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上沐飘,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天游桩,我揣著相機(jī)與錄音牲迫,去河邊找鬼。 笑死借卧,一個(gè)胖子當(dāng)著我的面吹牛盹憎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播铐刘,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼陪每,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了镰吵?” 一聲冷哼從身側(cè)響起檩禾,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎疤祭,沒想到半個(gè)月后盼产,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡勺馆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年戏售,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片草穆。...
    茶點(diǎn)故事閱讀 40,865評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蜈项,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出续挟,到底是詐尸還是另有隱情紧卒,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布诗祸,位于F島的核電站跑芳,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏直颅。R本人自食惡果不足惜博个,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望功偿。 院中可真熱鬧盆佣,春花似錦、人聲如沸械荷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吨瞎。三九已至痹兜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間颤诀,已是汗流浹背字旭。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工对湃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人遗淳。 一個(gè)月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓拍柒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親屈暗。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評論 2 361

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

  • 上半年有段時(shí)間做了一個(gè)項(xiàng)目恐锦,項(xiàng)目中聊天界面用到了音頻播放,涉及到進(jìn)度條陕贮,當(dāng)時(shí)做android時(shí)候處理的不太好,由于...
    DaZenD閱讀 3,021評論 0 26
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉潘飘,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,732評論 0 9
  • 本文結(jié)構(gòu)如下: Why? (為什么要用KVO) What? (KVO是什么) How? ( KVO怎么用) Mo...
    等開會閱讀 1,653評論 1 21
  • 前言: 本文基本不講KVC/KVO的用法肮之,只結(jié)合網(wǎng)上的資料說說對這種技術(shù)的理解卜录。 由于KVO內(nèi)容較少戈擒,而且是以KV...
    土b蘭博王閱讀 3,069評論 0 33
  • KVO屬性依賴 看一個(gè)例子:我們的模型類 LabColor 代表一種 lab色彩空間里的顏色。和 RGB 不同丑瞧,這...
    毅個(gè)天亮閱讀 758評論 0 1