平时我们最容易犯的一个错误就是循环引用,而且难以察觉,而FBRetainCycleDetector
则是专门在运行期来检查循环引用,那我们来看看他是怎么做到的。
开始
首先,我们来看看在arc环境下,什么时候会发生强引用。
- 自身属性,被定义为strong类型的变量,都会产生一次强引用。
- associate object,被定义为retain类型的也会被强引用。
- block,在被闭包捕获的时候,strong类型对象也会被强引用。
- 特殊对象,比如
NSTimer
的target
,集合类型的addObject
。
associate object
如何记录associate object的持有情况呢?这里要说一下C语言的hook,也就是在链接的时候替换掉objc_setAssociatedObject
方法,然后记录源对象和持有对象。关于hook可以参考fishhook这个库。
property
objc的对象会有自身的布局记录(layout),取出每个类中的Ivar的属性,就可以知道哪些属性是强引用的,也就可以知道每个对象所持有的对象了。
block
和property一样,每个block也是有对应的layout的。
其他
其他一些特殊情况,则需要特殊考虑,细节这里就不说明了。
算法
首先,我们将每个需要检测的对象视作一颗颗树,叶子是每个强引用的对象。
作者使用了堆栈来替换递归实现路径点的查找,基本原理是:
1 | // 堆栈用于保存所遍历过的路径 |
算法其实很简单,源码也就100行以内。
最后
这种方式给予我们一种能够在运行时检查循环引用的方法,但是这并不代表完全正确,比如CFArray,NSHashMap我们就无法判断子元素的引用情况。还有一些虽然形成了循环引用,但在整个流程的结尾,是必定会解除的,会形成误判。