kvo是objecC中常用的一种技术。
一句话来讲,就是对对象的属性进行观察。
当对象的属性发生变化时,会通知到观察者。
如下,有一个对象Person。
Person有一个属性age。
@interface Person : NSObject
@property (nonatomic, assign) int age;
@end
在一个其他的类中对Person的属性进行观察。
我这里是在viewController中,创建一个Person对象,并对age属性进行观察。
- (void)viewDidLoad {
[super viewDidLoad];
Person *zhangSan = [[Person alloc]init];
[zhangSan addObserver:self
forKeyPath:@"name"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionNew
context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
}
这样就完成了对一个Person对象zhangSan的监听。
zhangSan的age被setter方法重新赋值后,上面的监听函数就会执行。
如果zhangSan的age属性不是通过setter方法赋值,监听函数不会执行。比如
_age = 10;
这行代码不会触发监听函数的执行。
kvo实现的原理
我们应该也能想到,思路应该就是把对象的setter方法重写了。
当对象被监听的那个时候,动态的为对象生成了一个子类。
[zhangSan addObserver:self
forKeyPath:@"name"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionNew
context:nil];
当执行上述代码的时候,动态生成NSKVONotifying_Person这样一个Person的子类。
之后,再执行如下代码。
zhangSan.age = 10;
此时setAge:方法被重新定位到NSKVONotifying_Person中,它除了会给_age成员变量赋值之外,还会执行其他两个操作,如下。
- (void)willChangeValueForKey:(NSString *)key // 属性值变化之前执行
- (void)didChangeValueForKey:(NSString *)key // 属性值变化之后执行
// didChangeValueForKey:方法会执行下面的函数
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context;
kvo的注意事项
- 不移除会造成内存泄漏
- 多次重复移除会崩溃。原因是在移除的时候,系统会判断当前KVO的key是否已经被移除,如果已经被移除,则主动抛出一个NSException的异常
网友评论