block 循环引用终结者

block 循环引用终结者_第1张图片

block 循环引用的例子

先介绍一下这个 demo ( github 地址 ):有两个 view,从第一个 view 可以点击 button push 到第二个 view 中,并且我在第二个 view 中的方法 - (void)viewWillDisappear:(BOOL)animated- (void)dealloc 打印某句话来检测该方法是否被执行。

显然,上图会出现循环引用,导致第二个 view 无法释放。

控制台输出

控制台的输出也证实了这点,- (void)dealloc 并没有被执行。

block 循环引用终结者_第2张图片

它们之间的持有关系是这样的,self 持有 _testClass,而 _testClass 持有的 block 又通过持有 self 来捕捉变量 _kkString。这就导致了 block 的循环引用。

打破 block 循环引用的常规做法

打破 block 的常规做法就是 weak/strong dance,而且还要 block 里面对 wSelf 判空。具体请看例子。

block 循环引用终结者_第3张图片

如图,weak/strong dance 就是在 block 前加上 __weak typeof(self) wSelf = self;,而 block 里面再加上 __strong typeof(self) sSelf = wSelf;,并且注意到有时候还要要对 wSelf或者sSelf 判空,甚至还要出现多次判空的操作,SDWebImage 中就有相应的例子。

为什么要对 wSelf 判空

因为这是 block 的回调并不是马上执行,而是我延时 5 秒后执行,假设这 5 秒内我返回到前一页,当前的 self 就会被释放掉,下面的 _kkString 也会变为 nil,而向字典传 nil 会导致 crash,所以在 block 里面必须对 self 操作必须判空。当然,如果是方法的调用你不判空也没问题,因为对 nil 对象发送消息并不会导致 crash

为什么要调用 __strong typeof(self) sSelf = wSelf

简单来说就是防止在执行 block 里面的操作时,self 被释放掉。同样,可以看到截图中,假设不调用__strong typeof(self) sSelf = wSelf,并且在 block 里面做一个循环 100000 次的操作,在这个操作的过程中返回到前一个 view,此时 self 被释放,循环操作里面还有对 _kkString 的引用,这同样会导致 crash

block 循环引用终结者_第4张图片
控制台输出

控制台输出也证实这样不会导致循环引用。

虽然说 weak/strong dance 可以解决循环引用,但处理起来也是比较麻烦,那有没有一种简单的方法可以处理好 block 呢?

POP 的做法

block 循环引用终结者_第5张图片
pop/POPAnimation.h

其实一看这个 target 是很多余的,block 明明可以捕获上下文,为什么还要传 target 呢?

block 不会引用参数,那如果把 self 作为参数传入 block,再在原文中调用 target,这就把我们需要管理的释放交由系统处理。

block 循环引用终结者_第6张图片

传入 self 作为 target,而此时 block 再将 target 传出。当然,如果你 block 内不调用 target 的,可以传 nil 作为 targer。而回调操作如果要使用 self 则使用 target 代替。

P.S. 在和 bestswifter 讨论后才知道 iOS 程序猿也曾提过相应的做法,使用 Heap-Stack Dance 替代 Weak-Strong Dance,优雅避开循环引用。

当然这种做法也有缺陷,就是会 延长对象的生命周期,就如刚刚的例子,假设我在 5 秒内返回到前一个 view,第二个 view 并不会像使用了 weak/strong dance 做法那样立刻释放掉,而是等 block 里面操作执行完再释放。

控制台输出

总结

实践出真知,关于使用 weak/strong dance 或使 POP 的做法,就是看实际情况。

你可能感兴趣的:(block 循环引用终结者)