iOS - 在闭包或块中何时保留对self的强引用?
iOS - 在闭包或块中何时保留对self的强引用?
在WWDC 2014的403个会议中,介绍了Intermediate Swift和transcript, 有以下幻灯片:
发言者说,如果不在那里使用[unowned self]
,就会发生内存泄漏。这是否意味着我们应该始终在闭包内使用[unowned self]
?
在Swift Weather应用程序的ViewController.swift的第64行,我没有使用[unowned self]
。但是,我使用了一些@IBOutlet
,例如self.temperature
和self.loadingIndicator
来更新用户界面。这可能是可以的,因为我定义的所有@IBOutlet
都是weak
的。但是出于安全考虑,我们是否应该始终使用[unowned self]
?
class TempNotifier { var onChange: (Int) -> Void = {_ in } var currentTemp = 72 init() { onChange = { [unowned self] temp in self.currentTemp = temp } } }
2016年11月更新
我写了一篇关于这个话题的文章(通过了解SIL来理解ARC的操作),请在这里查看。
原始回答
之前的答案没有给出明确的规则来说明何时使用哪种引用类型以及为什么使用,因此让我补充一些东西。
关于 unowned 和 weak 的讨论可以最终归结为变量的生命周期和引用它的闭包的生命周期的一个问题。
场景
有两种可能的场景:
-
闭包的生命周期与变量相同,因此只有在变量处于可达状态时,闭包才是可达的。变量和闭包具有相同的生命周期。在这种情况下,应将引用声明为 unowned。一个常见的例子是
[unowned self]
,它用于许多在其父级上下文中执行某些操作的小闭包的示例,并且不被任何其他地方引用,因此不会超出其父级的生命周期。 -
闭包的生命周期与变量无关,当变量不再可达时,仍然可以引用闭包。在这种情况下,应将引用声明为 weak 并在使用之前验证它是否为 nil(不要强制解包)。一个常见的例子是你可以在某些与生命周期完全无关的代理对象中看到闭包引用的
[weak delegate]
。
实际应用
那么,大多数时候应该使用哪种类型呢?
引用 Twitter 中的 Joe Groff(请见此处):
unowned 更快,可以实现不可变性和非可选性。
如果不需要 weak,就不要使用它。
你可以在这里了解有关 unowned 的更多工作原理。
*
通常也称为 unowned(安全),以表示在访问 unowned 引用之前执行运行时检查(对于无效引用,这会导致崩溃)。
并不是所有情况下都需要使用[unowned self]
。有时候,你需要闭包捕获self以确保在调用闭包时它仍然存在。
示例:进行异步网络请求
如果你正在进行异步网络请求,你确实需要闭包保留self
,以便在请求完成时使用它。否则,该对象可能已被释放,但你仍然希望能够处理请求完成。
何时使用unowned self
或 weak self
只有当你需要避免强引用循环时,才需要使用[unowned self]
或[weak self]
。强引用循环是指一个循环拥有关系,使得对象最终互相拥有(可能是通过第三方),因此它们永远不会释放,因为它们互相确保对方保持存在。
在特定情况下,对于闭包中引用的任何变量,这些变量将被闭包“拥有”。只要闭包存在,这些对象就保证存在。唯一的解除所有权的方法是使用[unowned self]
或[weak self]
。因此,如果一个类拥有一个闭包,而该闭包捕获该类的强引用,则闭包和类之间存在一个强引用循环。如果该类拥有拥有闭包的某些东西,则也包括在内。
特别是在视频的示例中
在幻灯片示例中,TempNotifier
通过onChange
成员变量拥有该闭包。如果他们没有将self
声明为unowned
,则闭包也会拥有self
,从而创建强引用循环。
unowned
与weak
的区别
unowned
与weak
的区别在于,weak
声明为可选类型,而unowned
则不是。通过将其声明为weak
,您可以处理在闭包内部它可能为空的情况。如果尝试访问一个偶尔为空的unowned
变量,它将崩溃整个程序。因此,仅在确认该变量在闭包存在时始终存在时才使用unowned
。