swiftswiftSwift见解

Swift中解决引用循环之Unowned 与 Weak的选择

2017-11-22  本文已影响46人  BennyLoo

Swift的内存管理机制与Object-C一样,都是采用了自动内存管理 -- ARC。那么这样就不得不想到老生常谈的一个问题——引用循环

Object-C中,我们习惯使用__weak关键词来打破这样的循环,使循环引用的对象得到释放。而在Swift中,有两种方式可以做到同样的效果,它们分别是UnownedWeak关键词。尽管两者确实都能解决引用循环的问题,但是两者的使用场景稍有不同。

因为目的是解决引用循环的问题,那就block(闭包)的函数体中取消引用循环来讨论。

1.UnownedWeak的使用时机

Unowned一般使用在其所修饰的对象和所处的block环境的生命周期一致的时候。

简单来说,Unowned修饰的对象,在整个block的使用期间都应该是有效的,即不可为nil。

Weak则可以使用为block 的生命周期超出其对象进行修饰。

意思是可以修饰可能变成nil的对象。用Swift中的话语来说就是Optional对象。

相比之下,weak的使用范围更加广泛,如果不考虑性能的话,我们大可以无论什么情况都使用weak将会更加安全。然而,既然Unowned存在,必将有他的意义 —— 出于性能考虑,我们应该在可以使用Unowned的时候尽可能的使用Unowned,具体原因请往下看。

2.UnownedWeak的开销更小

Swift 中的每个对象保持了两个引用计数器,一个是强引用计数器,用来决定 ARC 什么时候可以安全地析构这个对象,另外一个附加的弱引用计数器,用来计算创建了多少个指向这个对象的 unowned 或者 weak 引用,当这个计数器为零时,这个对象将被析构 。

弱引用计数器在增加之前(这是一个原子操作),会事先检查一个对象的强引用计数器的值,当值大于0的时候,才确保这是有效的。不然我们访问一个无效的值将会引起程序运行时报错从而导致崩溃。

Unowned 在编译的时候,进行了优化,它不会对有效只进行辨别:如果其引用的对象是一个有效值,它将会顺利在弱引用计数器原本的基础之上加1。如果是一个无效的值,那它将指向一个垃圾内存中的地址,在运行的时候,这将会产生错误,这就是为什么我们需要在使用Unowned时候,要保证对象生命周期的有效性。

Weak针对Unowned 进行了包装(光凭这一点,Unowned 在性能上优于Weak)。Swift 的 weak 引用添加了附加层,间接地把 unowned 引用包裹到了一个可选容器里面,会对可选值进行相应的处理,这将带来一定的开销。所以尽管我们给一个nil对象添加Weak 修饰,程序运行中,依旧不会报错。

这里的性能对比,验证了上面所说的,我们应该在对象的生命周期允许的情况下,尽量使用Unowned

上一篇下一篇

猜你喜欢

热点阅读