前言
这篇文章仅仅回答weakSelf、stongSelf是如何做到避免循环引用也延长执行生命周期的。
这个小技巧可能都被iOS工程师们用到滚瓜熟烂了,但同时也很可能有人还不知道原理的,所以小智就在这简单介绍一下!
避免循环引用?
全世界都知道block会自动强引用里面调用的实例变量,SO,一旦block里面所直接引用了self就会引发循环引用,为啥呢?
如上图所示是一个直接强引用的示例图。OK,假设这时self想要释放自己,它的引用计数器必须清零,然后执行dealloc去释放掉它持有的属性与实例变量。
只是是不是看出点端倪来了。对了block是在dealloc的过程中执行的,在ARC环境下把这些代码都隐藏自动执行了。SO,因为block的强引用self,那么self的计数器就不会清零,就不会执行dealloc,不会执行dealloc,那么block的释放操作也不会执行,就形成了循环引用,造成self和block都不会被释放掉,内存泄漏。
那么引用引用了weakSelf呢?哈哈,如下图所示:
我们继续从引入计数来分析这个问题。block引用了weakSelf,因为是weakSelf所以self的引用计数器完全不受block的影响。所以self在执行dealloc是爱咋地就咋地,只要count清零,就立即执行,还顺便释放掉block,所以weakSelf是能让实现避免循环引用的问题。
延长执行生命周期?
好像有weakSelf就能解决block的所有问题,这延长执行生命周期有是什么鬼?
好的,我们又来假设一种新的情况,假如你的block是个网络请求回调,而且这个回调是在非主线程异步执行,那么如果这时的self的count清零了,block中的回调都还未执行完就被清零了,这有时还是会影响业务逻辑的,要解决这个问题就需要引用strongSelf了,如下图所示:
细心的大家会发现stringSelf变蓝了,为什么不是变黄呢?那时因为红色代表block外部定义的属性变量,而蓝色表示是block中自己定义的局部变量。来到这里只要理清两个概念,即可解开这个谜题,
- 一个指针对另外一个指针赋值,被赋值的指针实际上就是得到指向对象的地址
- 方法函数中的局部变量是存储在栈内存区的,所以一旦方法函数执行完,那么局部变量就会被释放掉。
根据以上两点得出上图的简略图:
首先block只是在block只引用了weakSelf,所以不会引起循环引用。其次,每当block在执行的时候,因为它的局部变量间接强引用self,所以self的count不会清零而执行dealloc,确保了block能执行完成,一旦block执行完成,self又具备执行dealloc的权利,那么就能安全释放。
总结:
根据上文的原理阐述,发现通过weakSelf与strongSelf搭配使用,能决定一个block是否需要延长生命周期确保执行完毕。