KVO簡介
KVO全稱Key-Value Observing(鍵值監(jiān)聽),當前對象某個屬性值發(fā)生改變時,監(jiān)聽該屬性值變化的對象可以接到通知。
KVO實現(xiàn)原理
通過OC強大的Runtime運行時機制實現(xiàn)的。當?shù)谝淮斡^察當前對象時,Runtime會創(chuàng)建一個當前類的子類。在這個子類中,它會重寫所有被觀察的key,然后將對象的isa指針指向新創(chuàng)建的子類。所以當前對象神奇的變成了新的子類的實例。這些被重寫的方法中添加了調(diào)用通知觀察者的方法的代碼。當一個對象的一個屬性改變時,會觸發(fā)setKey方法,但這個方法被重寫了,并且在內(nèi)部添加了發(fā)送通知機制。
簡單使用
//在Person.h中增加一個屬性age
@interface Person : NSObject
@property (nonatomic, assign) NSInteger age;
@end
// 在Dog.m中添加KVO監(jiān)聽方法observeValueForKeyPath:ofObject:change:context:
@implementation Dog
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
NSLog(@"%@監(jiān)聽到了%@對象的%@屬性的值改變了:%@",self ,object ,keyPath ,change);
}
@end
@interface ViewController ()
@property (nonatomic, strong) Person *person;
@property (nonatomic, strong) Dog *dog;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.person = [[Person alloc] init];
self.dog = [[Dog alloc] init];
self.person.age = 10;
// self.dog 監(jiān)聽self.person的age屬性
[self.person addObserver:self.dog forKeyPath:@"age" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
self.person.age = 50;
}
@end
我們運行程序,然后點擊空白屏幕,就會看到如下輸出,說明我們的確實已經(jīng)能夠使用KVO監(jiān)聽屬性變化了。

然后我們通過打斷點的方式,會看到在調(diào)用addObserver:forKeyPath:options:context:方法前后Person所屬類是不同的。
添加觀察器之前:

添加觀察器之后:

通過前后對比,我們發(fā)現(xiàn)當person對象被監(jiān)聽后,系統(tǒng)在運行時動態(tài)創(chuàng)建了一個繼承自Person的子類NSKVONOtifying_Person類。然后KVO會在這個派生類中,重寫基類中任何被觀察屬性的setter方法,在setter方法中實現(xiàn)真正的通知機制。
KVO是一個很強大的工具,有時候過于強大了,尤其是有了自動觸發(fā)通知機制?,F(xiàn)在我們知道了它的工作原理,知道使用它會在運行時創(chuàng)建一個新的類,所以性能會有一定影響,因此除非非要監(jiān)聽某個屬性值的變化時候才使用它。這些知識或許能幫助你更好地使用它,或在它出錯時更方便調(diào)試。
以上內(nèi)容摘錄借鑒于:KVO實現(xiàn)原理