KVO使用詳解

KVO

KVO 即 Key-Value Observing汞窗,翻譯成鍵值觀察。它是一種觀察者模式的衍生刊苍。其基本思想是既们,對(duì)目標(biāo)對(duì)象的某屬性添加觀察,當(dāng)該屬性發(fā)生變化時(shí)正什,通過觸發(fā)觀察者對(duì)象實(shí)現(xiàn)的KVO接口方法啥纸,來自動(dòng)的通知觀察者。

觀察者模式是什么 一個(gè)目標(biāo)對(duì)象管理所有依賴于它的觀察者對(duì)象婴氮,并在它自身的狀態(tài)改變時(shí)主動(dòng)通知觀察者對(duì)象斯棒。這個(gè)主動(dòng)通知通常是通過調(diào)用各觀察者對(duì)象所提供的接口方法來實(shí)現(xiàn)的。

簡(jiǎn)單來說KVO可以通過監(jiān)聽key莹妒,來獲得value的變化名船,用來在對(duì)象之間監(jiān)聽狀態(tài)變化。

KVO使用

注冊(cè)與解除注冊(cè)

如果我們已經(jīng)有了包含可供鍵值觀察屬性的類旨怠,那么就可以通過在該類的對(duì)象(被觀察對(duì)象)上調(diào)用以下兩個(gè)方法來注冊(cè)和解除注冊(cè)

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

observer: 被觀察者
keyPath:鍵路徑參數(shù)渠驼,描述將要觀察的屬性,相對(duì)于被觀察者
options:標(biāo)識(shí)KVO希望變化如何傳遞給觀察者鉴腻,可以使用|進(jìn)行多選(有四個(gè)選項(xiàng))
context:上下文內(nèi)存區(qū)迷扇,通常為nil

options所包括的內(nèi)容

NSKeyValueObservingOptionNew:change字典包括改變后的值
NSKeyValueObservingOptionOld:change字典包括改變前的值
NSKeyValueObservingOptionInitial:注冊(cè)后立刻觸發(fā)KVO通知
NSKeyValueObservingOptionPrior:值改變前是否也要通知(這個(gè)key決定了是否在改變前改變后通知兩次)

處理變更通知

每當(dāng)監(jiān)聽的keyPath發(fā)生變化了,就會(huì)在這個(gè)函數(shù)中回調(diào)爽哎。

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

在這里蜓席,change 這個(gè)字典保存了變更信息,具體是哪些信息取決于注冊(cè)時(shí)的 NSKeyValueObservingOptions课锌。

手動(dòng)KVO(禁用KVO)

KVO中厨内,當(dāng)被觀察的屬性改變時(shí),KVO被觸發(fā)渺贤。舉例如下:
KVO監(jiān)測(cè)Person類實(shí)例person的name屬性雏胃。當(dāng)name值改變時(shí),方法- setName:被調(diào)用志鞍。此時(shí)下面兩個(gè)方法會(huì)在運(yùn)行- setName:之前之后被調(diào)用瞭亮。

- (void)willChangeValueForKey:(NSString *)key
- (void)didChangeValueForKey:(NSString *)key

但是有時(shí)候我們并不是都需要每次改變屬性值的前后都調(diào)用這兩個(gè)方法,那么我們這時(shí)候就需要手動(dòng)KVO,先把這兩個(gè)方法的自動(dòng)調(diào)用給禁用,然后我們?cè)僮约菏謩?dòng)去實(shí)現(xiàn)KVO的通知固棚。代碼如下:

+ (BOOL)automaticallyNotifiesObserversForName{ //禁用KVO的自動(dòng)通知
    return NO;
}
- (void)setName:(NSString *)lComponent{
     if (_name == name) { 
        return; 
    } 
    [self willChangeValueForKey:@"name"];
    _name == name; 
    [self didChangeValueForKey:@"name"];}

上述代碼的automaticallyNotifiesObserversForName方法是選擇是否自動(dòng)通知统翩,我們只需返回NO仙蚜,系統(tǒng)就不會(huì)自動(dòng)通知,然后再在setName方法中我們手動(dòng)調(diào)用通知的兩個(gè)方法厂汗,甚至可以加上加上自己想要的判斷條件委粉。
針對(duì)非自動(dòng)通知的屬性,可以分別在變化之前和之后手動(dòng)調(diào)用如下方法(will在前面徽,did在后)來手動(dòng)通知觀察者:

(will/did)ChangeValueForKey:(will/did)ChangeValueForKey:withSetMutation:usingObjects:
(will/did)Change:valuesAtIndexes:forKey:

鍵值依賴(注冊(cè)從屬key)

有時(shí)候一個(gè)屬性的值依賴于另一對(duì)象中的一個(gè)或多個(gè)屬性艳丛,如果這些屬性中任一屬性的值發(fā)生變更,被依賴的屬性值也應(yīng)當(dāng)為其變更進(jìn)行標(biāo)記趟紊。因此,object 引入了依賴鍵碰酝。要為一對(duì)一關(guān)系自動(dòng)觸發(fā)通知霎匈,應(yīng)該重寫keyPathsForValuesAffectingValueForKey或?qū)崿F(xiàn)一個(gè)合適的方法,該方法遵循它為注冊(cè)依賴鍵定義的模式送爸。
例如铛嘱,fullName取決于firstName和lastName。返回fullName的方法可以寫成如下:

- (NSString *)fullName {
    return [NSString stringWithFormat:@“%@%@”袭厂,firstName墨吓,lastName];
}

當(dāng)firstName或lastName發(fā)生改變時(shí),必須通知觀察fullName屬性的程序纹磺,因?yàn)樗鼈冇绊戇@個(gè)屬性的值帖烘。一個(gè)解決方案是重寫keyPathsForValuesAffectingValueForKey來指定fullName屬性依賴于lastName和firstName。

+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
 
    NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
 
    if ([key isEqualToString:@"fullName"]) {
        NSArray *affectingKeys = @[@"lastName", @"firstName"];
        keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
    }
    return keyPaths;
}

通過重寫keyPathsForValuesAffecting<Key>也可以達(dá)到相同的效果橄杨。

+ (NSSet *)keyPathsForValuesAffectingFullName {
    return [NSSet setWithObjects:@"lastName", @"firstName", nil];
}

KVO和線程

一個(gè)需要注意的地方是秘症,KVO 行為是同步的,并且發(fā)生與所觀察的值發(fā)生變化的同樣的線程上式矫。沒有隊(duì)列或者 Run-loop 的處理乡摹。手動(dòng)或者自動(dòng)調(diào)用 -didChange... 會(huì)觸發(fā) KVO 通知。
所以采转,當(dāng)我們?cè)噲D從其他線程改變屬性值的時(shí)候我們應(yīng)當(dāng)十分小心聪廉,除非能確定所有的觀察者都用線程安全的方法處理 KVO 通知。通常來說故慈,我們不推薦把 KVO 和多線程混起來板熊。如果我們要用多個(gè)隊(duì)列和線程,我們不應(yīng)該在它們互相之間用 KVO惯悠。
KVO 是同步運(yùn)行的這個(gè)特性非常強(qiáng)大邻邮,只要我們?cè)趩我痪€程上面運(yùn)行(比如主隊(duì)列 main queue),KVO 會(huì)保證下列兩種情況的發(fā)生:
首先克婶,如果我們調(diào)用一個(gè)支持 KVO 的 setter 方法筒严,如下所示:

self.exchangeRate = 2.345;

KVO 能保證所有 exchangeRate 的觀察者在 setter 方法返回前被通知到丹泉。

KVO原理

1.automaticallyNotifiesObserversForKeyYES時(shí)注冊(cè)觀察屬性會(huì)生成動(dòng)態(tài)子類NSKVONotifying_XXX
2.動(dòng)態(tài)子類觀察的是setter方法
3.動(dòng)態(tài)子類重寫了觀察屬性的setter方法、dealloc鸭蛙、class摹恨、_isKVOA方法
- setter方法用于觀察鍵值
- dealloc方法用于釋放時(shí)對(duì)isa指向進(jìn)行操作
- class方法用于指回動(dòng)態(tài)子類的父類
- _isKVOA用來標(biāo)識(shí)是否是在觀察者狀態(tài)的一個(gè)標(biāo)志位
4.dealloc之后isa指向元類
5.dealloc之后動(dòng)態(tài)子類不會(huì)銷毀

最后編輯于
?著作權(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
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)丹壕。三九已至庆械,卻和暖如春,著一層夾襖步出監(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