KVO (Key-Value Observing) 的使用

KVO的定義

當指定的對象的屬性被修改時,允許對象接收通知的機制。
觀察對象的屬性時,要指定觀察屬性的名稱,還有一些其他的選項(比如,將新/舊值都發給我)

“我想要觀察你的fido屬性,如果它發生了變化就通知我”
當setFido方法被調用時,被觀察的對象就會發送消息給你:
“我的fido屬性有了一個新值”

KVO的好處

KVO機制很適合model模型和view視圖之間的通訊
例如:
模型類A創建屬性數據,在控制器中創建觀察者,一旦屬性發生改變觀察者就收到通知,KVO再在控制器使用回調方法中處理實現視圖B的更新

KVO的代碼實現

基本實現

 [logger addObserver:self forKeyPath:@“lastTime” options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil]; 
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
NSString *oldValue = [change objectForKey:NSKeyValueObservingOptionOld];
NSString *newValue = [change objectForKey:NSKeyValueObservingOptionNew];
}

使用context

當某個對象注冊為觀察者時,需要傳遞指針作為context。當接收變化的通知時,context會隨通知一起發送。
可以根據context判斷是子類的通知還是父類的通知

static int contextForKVO
 [logger addObserver:self forKeyPath:@“fido” options:0 context:&contextForKVO]; 
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if(context != &contextForKVO){
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:(void *)context];
    } else {
        // 處理變化
    }
}

顯式觸發通知

如果使用存取方法來設置屬性,那么系統會自動通知觀察者。
如果不使用存取方法,可以通過以下方法顯式觸發通知。

[self willChangeValueForKey:@“lastTime”];
_lastTime = now;
[self didChangeValueForKey”@“lastTime”];

獨立的屬性

不觀察_lastTime而想觀察_lastTimeString

[logger addObserver:self forKeyPath:@“lastTimeString” options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil]; 
//告訴系統_lastTime會影響_lastTimeString
+ (NSSet *)KeyPathsForValuesAffectingLastTimeString {
    return [NSSet setWithObject:@"lastTime"];
}

這樣,當_lastTime發生變化時,觀察者會收到_lastTimeString改變的通知。

KVO的實現原理

如果向某個對象發送addObserver:forKeyPath:options:context: 消息,這個方法可以:

  • 決定被觀察對象的類,并使用 objc_allocateClassPair() 函數給這個類定義一個新的子類KVONotifying_BNRTowel
  • 改變對象的isa指針,指向新的子類
  • 覆蓋被觀察對象的存取器,發送KVO消息。
- (void)setLocation:(NSPoint)locatiton {
    [self willChangeValueForKey:@"location"];
  [super setLocation:location];
  [self didChangeValueForKey:@"location"]; 
}

這個新的子類以及方法都會在運行時使用運行時函數定義。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容