释放双眼,带上耳机,听听看~!
使用KVO分三个步骤:
1 通过addObserver:forKeyPath:options:context:方法注册观察者,观察者可以接收keyPath属性的变化事件
Observer 被观察者对象 keyPath 需要观察的属性
options 参数
NSKeyValueObservingOptionNew 接收新值,默认为接收新值
NSKeyValueObservingOptionOld 接收旧值
NSKeyValueObservingOptionInitial 在注册时立即接收一次回调 在改变时也会发送通知
NSKeyValueObservingOptionPrior 改变之前发一次 改变之后发一次
Context 任意参数 可以用来标记 跟回调的context相对应
2 在观察者中实现observeValueForKeyPath:ofObject:change:context: 方法 当keyPath属性发生改变后,KVO回调这个方法来通知观察者。
change里面的kind
NSKeyValueChangeSetting //set
NSKeyValueChangeInsertion //容器类 插入的
NSKeyValueChangeRemoval //容器类 remove
NSKeyValueChangeReplacement //重新替换
3 当观察者不需要监听时,可以调用removeObserver:forKeyPath:方法将Kvo移除。 必须在观察者消失之前移除 否则会Crash
在调用addObserver方法后,KVO并不会对观察者进行强引用 所以要注意观察者的生命周期 否则会到这观察者被释放带来的Crash
KVO有两种触发模式 默认的是自动模式 下来我们说说手动模式
这个方法默认返回YES 返回yes时就是自动模式 返回NO时 就是手动模式 key 是要观察的属性 可以根据key进行动态调整
> 坑-> 不要全部返回NO 要根据key动态返回 不然? 瞬干爆炸 头皮发麻
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key;
当调整为手动模式的时候 改变被监听的对象的时候 是不会被监听到的(因为需要手动调用)
[objc willChangeValueForKey:@"keyPath"];
objc.xxxx = xxx;(没有这句话的时候 观察者也会接收到回调)
[objc didChangeValueForKey:@"keyPath"];
KVO的属性依赖 关联关系
//关联关系
+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
NSString *objKey;
if ([key isEqualToString:objKey]) {
keyPaths = [[NSSet alloc]initWithObjects:@"需要关联key1",@"需要关联key1", nil];
}
return keyPaths;
}
这样关联了以后 被关联的值发生改变以后也会被key的观察者监听到
kvo如何观察容器类
容器类:数组 字典。。。
当你需要改变容器类的值得时候通过这种法获取容器 然后进行改值
NSMutableArray *array = [objc mutableArrayValueForKey:@"key"];
[ array addObjc:xx]
这个 array 和你之前的array已经不一样了 所以他会被监听到
其他的容器类同上 自己探究
KVO的底层原理 —>底层没有开元 一切源于网络
- 创建一个该对象的子类
- 重写set方法
- 修改外界的isa指针
[objc addObserver:self forKeyPath:@"page" options:NSKeyValueObservingOptionNew context:nil];
当调用这个方法之后 objc的isa指针不会再指向objc 而是指向 NSKVONotyfing_objc
系统在这个方法里创建了一个继承与objc 的子类并且在里面重写了 keyPath的set方法 当keyPath属性发生变化时 会回调到observeValueForKeyPath:ofObject:change:context: 方法里面