你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS鍵值觀察KVO實例詳解

iOS鍵值觀察KVO實例詳解

編輯:IOS開發綜合

簡介

什麼是KVO?KVO是Key-Value Observing的簡稱,翻譯成中文就是鍵值觀察。這是iOS支持的一種機制,用來做什麼呢?我們在開發應用時經常需要進行通信,比如一個model的某個數據變化了,界面上要進行相應的變化,但是如果我們程序並不知道數據什麼時候會進行變化,總不能一直循環判斷有沒有變化吧,那麼就需要在數據變化時給controlller發送一個通知,告知我變化了,你可以更新顯示內容了,通知的方式有很多種,比如Notification也是其中一種方式,本文要講解的KVO也是其中一種很輕巧的方式。

他的實現機制為,為可能改變的數據增加一個觀察者,在上面的說法中這個觀察者就是controller,它去觀察這個數據有沒有發生變化,一旦發生變化,就會得到一個信號,從而獲取到變化的數據,進行自己要做的操作。

實例效果

\

如上圖所示,界面上設置兩個label,一個顯示名字,一個顯示分數。還有一個按鈕,用來修改分數,現在要做到點擊按鈕分數變化。

可能你會覺得很簡單,直接在按鈕的響應方法中將分數的label內容修改不就可以了嗎,確實如此,但是這裡我們不這麼做,而是使用KVO來完成。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPs7Sw8e0tL2o0ru49tGnyfrEo9DNo6zV4rj2xKPQzdPQwb249sr00NSjrNK7uPbOqtDVw/ujrNK7uPbOqrfWyv2ho2xhYmVs1eLKx7bByKHEo9DNtcTK/b7dwLS9+NDQz9TKvqGjPC9wPg0KPHA+z9bU2s7Sw8e4+NXiuPbKtcD9u6/By7XE0afJ+sSj0M3M7bzT0ru49rnbsuzV36OstqjS5c6qztLSqrnbsuzRp8n6xKPQzbXEt9bK/bHku6/H6b/2o6zV4sqxo6zI57n71eK49tGnyfrEo9DNtcS31sr9t6LJ+sHLseS7r6OsscjI59TasLTFpc/s06bW0Na7ttTEo9DNtcS31sr9yvTQ1L340NDQ3rjEo6xLVk/V4rj2u/rWxr7Nu+HX1LavuPi527Ls1d+3osvNzajWqqOsy7XV4rj2yvTQ1LHku6/By6OsxOPSqtf2yrLDtLLZ1/e4z7301/ahozwvcD4NCjxwPtPaysfO0sPH1Nq527Ls1d+1xEtWT7vYtfe6r8r91tC9+NDQz+DTprXEstnX96OsyOe5+87Sw8fK1bW9wcu31sr9seS7r7XEzajWqqOsxMfDtL7Nvau31sr9bGFiZWy1xNa1uPjQ3rjEzqq1scewtcS31sr9oaPV4tH5vs3Ktc/WwcvSu8zXS1ZPvPzWtbnbsuy1xMH3s8yjrLWxyLvX7rrzu7nIsdK7sr2+zcrH0saz/bnbsuzV36Ossru5/dKq1NrIt8q10OjSqtLGs/21xMqxuvLU2dLGs/2jrNLyzqrSxrP9uvO+zbK71Nm74crVtb2x5LuvtcTNqNaqwcuhozwvcD4NCjxoMSBpZD0="實現方式">實現方式

上面例子中進行了一套KVO鍵值觀察的流程,我們整理一下進行了哪些工作:

設計界面樣式 建立學生模型 對學生的分數屬性添加觀察 修改學生的分數屬性 在觀察到變化的響應方法中進行界面更新操作 不再需要觀察的時候移除觀察

現在通過這個例子來一步步講解。

設計樣式

樣式就不說了,兩個label,一個按鈕,以及按鈕的響應方法,都是很常見的。

建立模型

這個部分,就是新建一個NSObject類,用來作為學生模型,有兩個屬性:姓名和分數,如下所示:

// StudentModel.h
@interface StudentModel : NSObject

@property (nonatomic, copy) NSString *name;
@property float score;

@end

// ViewController.m
// 在controller中實例化學生模型
self.studentModel = [[StudentModel alloc] init];
[self.studentModel setValue:@"Cloudox" forKey:@"name"];
[self.studentModel setValue:@"89.0" forKey:@"score"];

添加觀察

這一步,才是真正開始使用KVO了。

要使用KVO,至少必須要實現兩個方法:

addObserver:forkeyPath:options:context: observeValueForKeyPath:ofObject:change:context:

第二個方法,就是用來獲取數據變化的通知並進行相應操作的方法,這個我們後面再講,先講第一個方法,顧名思義,這就是用來添加觀察者的方法了。

可能你會注意到,我們上面實例化學生模型的時候,使用的是 setVlue:forKey: 的形式來設置屬性值的,為什麼要這樣設置呢?聯想到KVO的名字,鍵值觀察,就能大概明白了,學生模型的屬性名就相當於key,屬性值就相當於值。

緊接著就可以對分數來添加觀察了:

[self.studentModel addObserver:self forKeyPath:@"score" options:NSKeyValueObservingOptionNew             context:nil];

這裡使用的就是第一個方法,有四個參數。

第一個參數是觀察者,這裡被觀察者是學生模型,觀察者是controller,也就是self 第二個參數是keyPath,其實也就是要觀察的鍵 第三個是一個options,這裡我們寫的是一個枚舉值,這個地方可以填幾種值,下面在進行詳細的說明 第四個我們填了nil,也有其作用,下面再細說

總之通過這行代碼,我們就對score這個鍵,也即是分數添加了觀察。

修改數據

在按鈕的響應方法中修改學生模型的分數數據,同樣使用 setVlue:forKey: 的方式進行設置。

接收通知

這裡就用到第二個方法:observeValueForKeyPath:ofObject:change:context:

先看看這個例子中的實現:

// KVO回調
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqualToString:@"score"]) {
        self.scoreLabel.text = [NSString stringWithFormat:@"Score:%@", [self.studentModel valueForKey:@"score"]];
    }
}

可以看到這個回調的變化響應方法也有四個參數,其實就對應上面添加觀察時的四個參數,通過keyPath,我們可以判斷是不是我們想要接收的數據變化,判斷它是不是score,其實也就是對不同的被觀察者進行不同的操作。確實是分數變化後,我們就更新界面上的分數label,用新的分數來顯示。

移除通知

移除通知的方法很簡單,如下:

[self.studentModel removeObserver:self forKeyPath:@"score"];

從觀察者那邊移除對被觀察者特定鍵的觀察。

至此,一個簡單的KVO流程就走完了,很簡單對吧。

進階用法

傳遞對象

上面添加觀察者和響應變化的方法中都有一個 context參數,通過這個參數可以傳遞一些東西,在添加觀察者時設置要傳遞的內容,在響應變化時獲得傳遞的內容。

比如我要傳遞一個字符串,在添加觀察者時設置:

[self.studentModel addObserver:self forKeyPath:@"score" options:NSKeyValueObservingOptionNew context:@"heyMe"];// 2.通過context傳遞內容給觀察者

這裡在context的參數中就直接設置了要傳遞的對象字符串。然後在變化響應時:

// KVO回調
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqualToString:@"score"]) {
        self.scoreLabel.text = [NSString stringWithFormat:@"Score:%@", [self.studentModel valueForKey:@"score"]];
    }
    NSLog(@"%@", context);// 通過context獲取被觀察者傳遞的內容
}

這裡就可以輸出context看一下,會得到傳遞過來的字符串內容。

options參數

在添加觀察者時有一個options參數,在回調獲取變化時有一個change參數,這兩個參數其實是對應的,都是用來增加傳遞變化的豐富度。

options參數可以設為:

NSKeyValueObservingOptionOld:這表示在回調獲取變化時可以通過change參數獲取變化之前的值; NSKeyValueObservingOptionNew:這表示在回調獲取變化時可以通過change參數獲取變化後的值; NSKeyValueObservingOptionInitial:在添加觀察者方法return的時候就發出一次通知; NSKeyValueObservingOptionPrior:會在觀察的值發生變化前發出一次通知,變化後還是會發出一次通知,所以變化一次一共會得到兩次通知。

以上就是options參數,可以看到都是對應change參數的,用來決定change參數可以得到什麼樣的數據,在回調獲取變化時可以輸出change看一下,就可以知道不同的效果了。

change參數

在使用change的時候可以通過下面的key來操作:

NSKeyValueChangeKindKey:對應NSKeyValueChange的枚舉值
NSKeyValueChangeSetting = 1:說明被觀察的數據的setter方法被調用了; NSKeyValueChangeInsertion = 2:當觀察的數據是集合時,且對它進行insert操作時會返回該值; NSKeyValueChangeRemoval = 3:當觀察的數據是集合時,且對它進行remove操作時會返回該值; NSKeyValueChangeReplacement = 4:當觀察的數據是集合時,且對它進行replace操作時會返回該值。 NSKeyValueChangeNewKey:對應options參數中的NSKeyValueObservingOptionNew,會在其中包含觀察的數據變化後的新值 NSKeyValueChangeOldKey:對應options參數中的NSKeyValueObservingOptionOld,會在其中包含觀察的數據變化之前得舊值 NSKeyValueChangeIndexesKey:當NSKeyValueChangeKindKey是2、3、4的時候,也就是說是觀察集合數據時,這個key的值是一個NSIndexSet,包含操作對象的索引集合 NSKeyValueChangeNotificationIsPriorKey:包含一個布爾值,如果options的參數是NSKeyValueObservingOptionPrior,也就是會通知兩次,在第一次通知,也就是改變前的通知時,會包含這個key

關於這些change的值,都可以輸出到控制台試一下看看是什麼效果,會有更加直觀的感受。

手動通知

之前說的都是自動通知,當添加了觀察者後,只要發生改變就會自動通知觀察者,但有時候我們並不是什麼改變都希望得到通知,或者有時候是希望變化到什麼情況後再通知,這就需要改變通知的機制。默認的實現模式為自動通知的模式,要自定義何時進行通知,就要改成手動通知的模式。

要改成手動通知,首先要在被觀察者的模型中重寫一個方法 automaticallyNotifiesObserversForKey :

// StudentModel.m
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
    BOOL automatic = NO;
    if ([key isEqualToString:@"score"]) {
        automatic = NO;
    } else {
        automatic = [super automaticallyNotifiesObserversForKey:key];
    }
    return automatic;
}

這裡我們在學生模型的實現文件中重寫了這個方法,判斷當觀察的key是score分數時,就將自動通知關閉,其余的情況還是根據父類來進行判斷,這樣寫比較保險。

這樣在我們改變學生模型的分數時,就不會自動觸發通知了,要觸發通知,需要自己進行設置:

// 按鈕響應
- (void)changeScore {
    [self willChangeValueForKey:@"score"];// 改為手動通知
    [self.studentModel setValue:@"99.0" forKey:@"score"];
    [self didChangeValueForKey:@"score"];// 改為手動通知
}

這時就可以觸發通知了,如果一個操作會觸發多個屬性改變,都要發通知,那麼需要嵌套通知:

// 按鈕響應
- (void)changeScore {
    [self willChangeValueForKey:@"name"];// 改為手動通知
    [self willChangeValueForKey:@"score"];// 改為手動通知
    [self.studentModel setValue:@"Cloud" forKey:@"name"];
    [self.studentModel setValue:@"99.0" forKey:@"score"];
    [self didChangeValueForKey:@"score"];// 改為手動通知
    [self didChangeValueForKey:@"name"];// 改為手動通知
}

而在一個一對多的關系中,比如集合,你必須注意不僅僅是這個key改變了,還有它改變的類型以及索引,也就是我們change中對應的幾種涉及到集合的東西,如下所示:

 - (void)removeTransactionsAtIndexes:(NSIndexSet *)indexes {
        [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:@"transactions"];

           // Remove the transaction objects at the specified indexes.

        [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:@"transactions"];
}

鍵值依賴

其實關於KVO還有一個重要的點是鍵值依賴,也就是說一個屬性的值依賴於對象中的其他屬性,當那些屬性變化後,這個屬性的值自動被通知到進行修改,不過這個點沒太弄明白,蘋果給的例子有點不清不楚的,再研究一下吧。


示例工程:https://github.com/Cloudox/KVODemo

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved