KVO是key-value-observer的缩写,意思是:当观察的对象的某个属性值发生变化的时候,会通知观察者;是一种观察者的设计模式。
首先就是KVO的使用,我们可以使用
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
这个方法来对对象添加观察者,比如我有一个Person类,有一个name的属性,那么我们可以这样
[person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil]
为Person类添加观察者,这样可以观察person对象的name属性值得变化,然后我们可以通过下面的方法获取值得改变
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSLog(@"keyPath = %@, object = %@,change = %@", keyPath, object, change);
}
当然,在使用完后记得移除观察者
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));
-
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
上面是系统提供的两个移除的方法,系统推荐使用的是上面那个带有上下文context的。下面是这个例子的代码
- (void)viewDidLoad {
[super viewDidLoad];
Person *p1 = [[LJPerson alloc] init];
p1.name = @"123";
NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
[p1 addObserver:self forKeyPath:@"name" options:options context:nil];
p1.name = @"456";
[p1 removeObserver:self forKeyPath:@"name"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSLog(@"keyPath = %@, object = %@,change = %@", keyPath, object, change);
}
上面这种是通过setter方法来触发的,我们也可以通过KVC来触发KVO,比如这样
Person *p1 = [[LJPerson alloc] init];
[p1 setValue:@10 forKey:@"age"];
NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
[p1 addObserver:self forKeyPath:@"age" options:options context:nil];
[p1 setValue:@22 forKey:@"age"];
[p1 removeObserver:self forKeyPath:@"age"];
这也就是说明,我们不仅可以用KVO来观察对象属性值得变化,也可以用来观察成员变量值得改变。
接下来就是KVO的底层实现原理(网上抄袭和自身测试):
KVO是以runtime为基础的,在我们对某个对象添加观察者时,以上面的Person类为例,观察person对象的name属性时,runtime会创建一个继承与Person类的子类NSKVONotifiying_Person类,然后将指向person的isa指针指向这个新建的NSKVONotifiying_Person类,在这个类里面,会重写观察的属性的set方法,在这个方法里面调用willChangeValueForkey方法和didChangeValueForKey方法来达到监听的目的。
上面这个是通过setter方法来实现观察者模式的原理,至于KVC模式触发KVO的原理就不懂了,应该是系统内容对KVC进行一系列处理来达到监听目的的。
最后,一定要记得移除观察者,否则会导致崩溃。