KVO 作为 iOS 中一种强大并且有效的机制,为我们检测对象属性的变化提供了帮助;
但系统提供的KVO接口实在太麻烦,所以开发过程中,我们使用了facebook 开源的KVOController。
今天和大家分享一下在项目开发过程中KVOController的填坑经历。
第一个坑:
这样使用会导致self和KVOController形成循环引用。
1. KVOController为所有的NSObject对象都提供 -KVOController 属性,所以这里self是强引用了KVOController
2. FBKVOController 实例方法-observer:keyPath:options:block: 调用后会强引用 self
[_objectInfosMap setObject:infos forKey:object];
这里的 object 就是 -[KVOController observer:keyPath:options:block:] 传进来的第一个参数,也就是"self"
也就是把 “self” 作为 key,保存在FBKVOController的私有变量_objectInfosMap里面
就会形成 “self ----》 KVOController ----》 self” 这样的循环引用
第二个坑:
为了解决上面的循环引用问题,我把代码改成了下面酱紫,iOS10必崩
KVOControllerNonRetaining 与 KVOController 的区别就是初始化的时候,retainObserved 为 NO
上面“坑一”说到,“self” 是被 _objectInfosMap 强引用了,retainObserved 传 NO,就可以解决这个问题
但是事情并没有这样结束。接下来的测试,ios10的系统必崩
这是 "self" 释放了,但是没有调用到 removeObserver 的崩溃栈
在 -[FBKVOController _unobserveAll] 里面调试,发现 _objectInfosMap 中已经没有了 “self”
调用顺序是这样的:
1. [self dealloc]
2. [FBKVOController dealloc]
3. [FBKVOController dealloc] 里面找存起来的"self",并调用 [self removeObserver]
因为第3步中,“self”已经被释放,FBKVOController 中对“self” 的 weak 指针已经置 nil
所以最终并没有调用到 “self” 的 removeObserver
总结:
一、对象监听自己的属性变化,应该在setter中监听;而不应该使用 -[self.KVOController observe:self]
二、对象dealloc时,指向对象的weak指针已经置为nil