刨根問底KVO
KVO 全稱 Key-Value Observing。中文叫鍵值觀察。KVO實際上是一種觀察者模式,觀察者在鍵值改變時會獲得通知,利用它能夠很容易實現視圖組件和數據模型的分離,當數據模型的屬性值改變以後做爲監聽器的視圖組件就會被激發,激發時就會回調監聽器自身。相比Notification,KVO更加的簡單直接。html
KVO的操做方法由NSKeyValueCoding提供,而他是NSObject的類別,也就是說ObjC中幾乎全部的對象都支持KVO操做。app
KVO的使用也很簡單,就是簡單的3步。函數
- 註冊須要觀察的對象的屬性addObserver:forKeyPath:options:context:
- 實現observeValueForKeyPath:ofObject:change:context:方法,這個方法當觀察的屬性變化時會自動調用.在這個方法中還經過NSKeyValueObservingOptionNew這個參數要求把新值在dictionary中傳遞過來。
- 取消註冊觀察removeObserver:forKeyPath:context:
咱們觀察下代碼實現,探究實現原理:ui
Person.hatom
1 2 3 4 5 6 7 8 9
|
#import <Foundation/Foundation.h>
@interface Person : NSObject
- (void)registerObserver;
@property (nonatomic, assign) NSInteger age;
@end
|
Person.mspa
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
#import "Person.h"
@implementation Person
- (void)registerObserver { [self addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil]; }
- (NSString *)description { return [NSString stringWithFormat:@"%@,%ld",[self valueForKey:@"isa"],self.age]; }
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context { if ([keyPath isEqualToString:@"age"]) { NSLog(@"%@",change); } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } }
- (void)dealloc { [self removeObserver:self forKeyPath:@"age"]; }
|
main函數指針
1 2 3 4 5 6 7 8
|
Person * p = [[Person alloc] init]; p.age = 20; NSLog(@"%@",p);
[p registerObserver];
p.age = 30; NSLog(@"%@",p);
|
運行程序打印出的log日誌爲:日誌
1 2 3 4 5 6 7
|
Person,20 { kind = 1; new = 30; old = 20; } NSKVONotifying_Person,30
|
不要太驚訝,咱們慢慢解釋。code
我重寫的Person
類的description
方法,使用KVC拿到isa
指針,獲取到self的類名,咱們發如今使用KVO以前類名是Person
,但註冊了KVO以後,類名變成了NSKVONotifying_Person
。orm
主要是由於KVO的實現使用了isa-swizzling
。在程序運行時Person
會生成一個派生類NSKVONotifying_Person
,在這個派生類中重寫基類中任何被觀察屬性的setter
方法,用來欺騙系統頂替原先的類。在setter
方法中實現真正的通知機制.
咱們又能夠猜想,使用KVO,內部必定執行setter方法。
當咱們把上面代碼 p.age = 30;
改爲p->_age = 30;
你會發現KVO的方法不走了,也證明了這點。
若是p不提供setAge,getAge方法,還想用KVO.
1 2 3
|
[p willChangeValueForKey:@"age"]; p->_age = 30; [p didChangeValueForKey:@"age"];
|
蘋果官方KVO文檔:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html
另外.....
個人願望是.......
世界和平.........