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"];
}
這個新的子類以及方法都會在運行時使用運行時函數定義。