在使用swift的闭包时的一种常见的循环引用
什么是循环引用?
循环引用是指在程序中,两个互相持有对方强引用的对象,无法正常释放,从而导致内存泄漏。这在
Block
以及代理的使用中都很常见。
可以先看一下以下代码,你能发现哪里有问题吗?
1 | class RetainCycle { |
如果你注意过内存泄漏的问题,那么你可以很快发现这里面出现了一个循环引用。
在RetainCycle
中,通过定义属性的方式持有了一个字符串变量,以及一个没有参数以及返回值的闭包,RetainCycle对他们都是强引用。而在init()
方法中,我们在closure
的实现里通过self
取到了str
变量,并对它进行赋值。此时闭包closure
捕获了self
,并对它持有强引用。
由于RetainCycle
和closure
同时都持有了对方的强引用,就导致了RetainCycle
的实例无法正常释放,这时我们的deinit
方法是不会执行的,因为内存一直被持有无法释放。
那么如何解决这个循环引用的问题呢?
很简单,我们只需要在closure
捕获self
之前,使用unowned
或weak
关键字让它持有的是self
的弱引用,这样就不会导致循环引用的问题了。
1 | class RetainCycle { |
运行后我们可以在控制台看到deinit
方法打印的”deInit”,说明RetainCycle
实例已经被正常释放了。
weak和unowned的区别?
- weak:在引用的对象被释放后,标记为
weak
的内容会自动地被置为nil
,因此被标记为weak
的变量一定是Optional
值 - unowned:
unowned
设置以后即使它引用的内容已经被释放了,它仍然会对被已经释放了的对象持有一个”无效的”引用,它不能是Optional
值,也就是说不能被置为nil
。这时候你通过这个引用去访问成员属性或者调用方法的话,就会造成程序崩溃。
结语
循环引用的问题是在开发过程中常见的问题,并且在面试中似乎也是面试官喜欢问的知识点。在block、delegate、closure的使用中我们都要注意防止循环引用的出现。同时,在两个类中同时已属性的方式持有对方,也会造成循环引用。
而内存泄漏,是导致程序性能差的一大元凶
Tino Wu