我们是否应该在Swift的闭包内部始终使用[unown self]

本文翻译自:Shall we always use [unowned self] inside closure in Swift

In WWDC 2014 session 403 Intermediate Swift and transcript , there was the following slide 在WWDC 2014会议403 Intermediate Swift成绩单中 ,有以下幻灯片

在此处输入图片说明

The speaker said in that case, if we don't use [unowned self] there, it will be a memory leak. 发言者说,在这种情况下,如果我们不在那里使用[unowned self] ,那将导致内存泄漏。 Does it mean we should always use [unowned self] inside closure? 这是否意味着我们应该在闭包内部始终使用[unowned self]

On line 64 of ViewController.swift of the Swift Weather app , I don't use [unowned self] . Swift Weather应用程序的ViewController.swift的第64行上 ,我不使用[unowned self] But I update the UI by using some @IBOutlet s like self.temperature and self.loadingIndicator . 但是我通过使用一些@IBOutlet来更新UI,例如self.temperatureself.loadingIndicator It may be OK because all @IBOutlet s I defined are weak . 可能没问题,因为我定义的所有@IBOutletweak But for safety, should we always use [unowned self] ? 但是为了安全起见,我们应该始终使用[unowned self]吗?

class TempNotifier {
  var onChange: (Int) -> Void = {_ in }
  var currentTemp = 72
  init() {
    onChange = { [unowned self] temp in
      self.currentTemp = temp
    }
  }
}

#1楼

参考:https://stackoom.com/question/1e2pf/我们是否应该在Swift的闭包内部始终使用-unown-self


#2楼

No, there are definitely times where you would not want to use [unowned self] . 不,在某些情况下,您肯定不想使用[unowned self] Sometimes you want the closure to capture self in order to make sure that it is still around by the time the closure is called. 有时您希望闭包捕获自身,以确保在调用闭包时它仍然存在。

Example: Making an asynchronous network request 示例:发出异步网络请求

If you are making an asynchronous network request you do want the closure to retain self for when the request finishes. 如果发出异步网络请求,则确实希望闭包在请求完成时保留self That object may have otherwise been deallocated but you still want to be able to handle the request finishing. 否则该对象可能已被释放,但是您仍然希望能够处理请求完成。

When to use unowned self or weak self 何时使用unowned selfweak self

The only time where you really want to use [unowned self] or [weak self] is when you would create a strong reference cycle . 真正要使用[unowned self][weak self]的唯一时间是创建一个强大的参考周期 A strong reference cycle is when there is a loop of ownership where objects end up owning each other (maybe through a third party) and therefore they will never be deallocated because they are both ensuring that each other stick around. 一个强大的参考周期是,当所有权循环出现时,对象最终彼此拥有(可能是通过第三方),因此,由于它们都确保彼此粘在一起,因此永远不会将它们释放。

In the specific case of a closure, you just need to realize that any variable that is referenced inside of it, gets "owned" by the closure. 在闭包的特定情况下,您只需要意识到闭包内部“引用”了其中引用的任何变量。 As long as the closure is around, those objects are guaranteed to be around. 只要闭包在周围,这些对象就可以保证在周围。 The only way to stop that ownership, is to do the [unowned self] or [weak self] . 阻止该所有权的唯一方法是执行[unowned self][weak self] So if a class owns a closure, and that closure captures a strong reference to that class, then you have a strong reference cycle between the closure and the class. 因此,如果一个类拥有一个闭包,并且该闭包捕获对该类的强引用,那么在闭包和该类之间便拥有一个强引用循环。 This also includes if the class owns something that owns the closure. 这还包括类是否拥有某个拥有闭包的东西。

Specifically in the example from the video 特别是在视频示例中

In the example on the slide, TempNotifier owns the closure through the onChange member variable. 在幻灯片的示例中, TempNotifier通过onChange成员变量拥有闭包。 If they did not declare self as unowned , the closure would also own self creating a strong reference cycle. 如果他们没有宣布selfunowned ,封闭也将自己的self创造一个有力的参考周期。

Difference between unowned and weak unownedweak之间的区别

The difference between unowned and weak is that weak is declared as an Optional while unowned is not. unownedweak之间的区别在于, weak被声明为可选, unowned则不是。 By declaring it weak you get to handle the case that it might be nil inside the closure at some point. 通过声明它是weak您可以处理某些情况下它在闭包内部可能为零的情况。 If you try to access an unowned variable that happens to be nil, it will crash the whole program. 如果您尝试访问一个恰好为nil的unowned变量,它将使整个程序崩溃。 So only use unowned when you are positive that variable will always be around while the closure is around 因此,只有当您肯定变量在闭包周围时将始终存在时,才使用unowned


#3楼

If self could be nil in the closure use [weak self] . 如果self在封闭中可能为零,请使用[weak self]

If self will never be nil in the closure use [unowned self] . 如果self在封闭中永远不会为零,请使用[unown self]

The Apple Swift documentation has a great section with images explaining the difference between using strong , weak , and unowned in closures: Apple Swift文档中有一个很棒的部分,其中的图像说明了在闭包中使用strongweakunown之间的区别:

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html


#4楼

Update 11/2016 更新11/2016

I wrote an article on this extending this answer (looking into SIL to understand what ARC does), check it out here . 我写了一篇有关扩展此答案的文章(研究SIL以了解ARC的作用),请在此处查看

Original answer 原始答案

The previous answers don't really give straightforward rules on when to use one over the other and why, so let me add a few things. 前面的答案并没有真正给出何时何时使用另一个为什么的简单明了的规则,所以让我补充一些内容。

The unowned or weak discussion boils down to a question of lifetime of the variable and the closure that references it. 无主或虚弱的讨论可以归结为变量的生存期以及引用它的闭包的问题。

迅速虚弱vs无主

Scenarios 情境

You can have two possible scenarios: 您可以有两种可能的情况:

  1. The closure have the same lifetime of the variable, so the closure will be reachable only until the variable is reachable . 闭包与变量具有相同的生存期,因此闭包只有在变量可达之前才可以到达 The variable and the closure have the same lifetime. 变量和闭包具有相同的生存期。 In this case you should declare the reference as unowned . 在这种情况下,您应该将引用声明为Unown A common example is the [unowned self] used in many example of small closures that do something in the context of their parent and that not being referenced anywhere else do not outlive their parents. 一个常见的例子是在许多小的闭包示例中使用的[unowned self] ,这些闭包在其父的上下文中执行某项操作,并且在其他任何地方都没有被引用而不会超过其父辈。

  2. The closure lifetime is independent from the one of the variable, the closure could still be referenced when the variable is not reachable anymore. 闭包生存期独立于变量之一,当变量不再可用时,仍可以引用闭包。 In this case you should declare the reference as weak and verify it's not nil before using it (don't force unwrap). 在这种情况下,您应该将引用声明为引用,并在使用它之前确认它不是零(不要强行打开包装)。 A common example of this is the [weak delegate] you can see in some examples of closure referencing a completely unrelated (lifetime-wise) delegate object. 一个常见的示例是[weak delegate]您可以在某些闭包示例中看到它们引用一个完全不相关的(按生命周期而言)的委托对象。

Actual Usage 实际用法

So, which will/should you actually use most of the times? 那么,您实际上会/应该在大多数时间使用?

Quoting Joe Groff from twitter : 从推特引用乔·格罗夫

Unowned is faster and allows for immutability and nonoptionality. 无所有权的速度更快,并允许不变性和非选择性。

If you don't need weak, don't use it. 如果您不需要弱点,请不要使用它。

You'll find more about unowned * inner workings here . 您可以在此处找到有关无主*内部工作原理的更多信息。

* Usually also referred to as unowned(safe) to indicate that runtime checks (that lead to a crash for invalid references) are performed before accessing the unowned reference. * 通常也称为无主(安全),表示在访问无主引用之前执行了运行时检查(导致无效引用崩溃)。


#5楼

Here is brilliant quotes from Apple Developer Forums described delicious details: 以下是来自苹果开发者论坛的精彩语录,描述了美味的细节:

unowned vs unowned(safe) vs unowned(unsafe) unownedunowned(safe)unowned(unsafe)

unowned(safe) is a non-owning reference that asserts on access that the object is still alive. unowned(safe)是一个非所有者引用,在访问时断言该对象仍然有效。 It's sort of like a weak optional reference that's implicitly unwrapped with x! 有点像一个弱的可选引用,它用x!隐式解开了x! every time it's accessed. 每次访问时。 unowned(unsafe) is like __unsafe_unretained in ARC—it's a non-owning reference, but there's no runtime check that the object is still alive on access, so dangling references will reach into garbage memory. unowned(unsafe)就像ARC中的__unsafe_unretained一样,它是一个非所有者的引用,但是没有运行时检查该对象在访问时是否仍然有效,因此,悬空的引用将进入垃圾内存。 unowned is always a synonym for unowned(safe) currently, but the intent is that it will be optimized to unowned(unsafe) in -Ofast builds when runtime checks are disabled. unowned始终是一个同义词unowned(safe)目前,但目的是,它会进行优化,以unowned(unsafe)-Ofast当运行时检查被禁用的构建。

unowned vs weak unownedweak

unowned actually uses a much simpler implementation than weak . 实际上, unowned使用比weak使用简单得多的实现。 Native Swift objects carry two reference counts, and unowned references bump the unowned reference count instead of the strong reference count . 本机Swift对象带有两个引用计数, unowned引用会颠覆无主引用计数,而不是引用计数 The object is deinitialized when its strong reference count reaches zero, but it isn't actually deallocated until the unowned reference count also hits zero. 当对象的引用计数达到零时,该对象将被取消初始化 ,但是直到未拥有的引用计数也达到零时才真正释放该对象。 This causes the memory to be held onto slightly longer when there are unowned references, but that isn't usually a problem when unowned is used because the related objects should have near-equal lifetimes anyway, and it's much simpler and lower-overhead than the side-table based implementation used for zeroing weak references. 这将导致扶住当有无主引用稍长的内存,但是这通常不是一个问题,当unowned时使用,因为相关的对象应该有接近相等的寿命无论如何,这是比简单得多和低开销用于弱引用清零的基于边表的实现。

Update: In modern Swift weak internally uses the same mechanism as unowned does . 更新:在现代Swift中, weak内部使用与unowned相同的机制 So this comparison is incorrect because it compares Objective-C weak with Swift unonwed . 所以这个比较是不正确的,因为它比较了Objective-C的weak和Swift的unonwed

Reasons 原因

What is the purpose of keeping the memory alive after owning references reach 0? 在拥有的引用达到0后保持内存活动的目的是什么? What happens if code attempts to do something with the object using an unowned reference after it is deinitialized? 如果代码在初始化后尝试使用未拥有的引用对该对象执行某些操作,会发生什么情况?

The memory is kept alive so that its retain counts are still available. 内存保持活动状态,因此其保留计数仍然可用。 This way, when someone attempts to retain a strong reference to the unowned object, the runtime can check that the strong reference count is greater than zero in order to ensure that it is safe to retain the object. 这样,当有人尝试保留对未拥有对象的强引用时,运行时可以检查强引用计数是否大于零,以确保可以安全地保留对象。

What happens to owning or unowned references held by the object? 对象拥有的拥有或未拥有的引用会发生什么? Is their lifetime decoupled from the object when it is deinitialized or is their memory also retained until the object is deallocated after the last unowned reference is released? 当对象被取消初始化时,它们的生命周期是否与对象分离了?或者在释放最后一个无主引用之后对象被重新分配之前,它们的内存也被保留了吗?

All resources owned by the object are released as soon as the object's last strong reference is released, and its deinit is run. 对象的最后一个强引用释放后,对象的所有资源都将释放,并且其deinit将运行。 Unowned references only keep the memory alive—aside from the header with the reference counts, its contents is junk. 无主引用仅使内存保持活动状态-除了带有引用计数的标头之外,其内容是垃圾。

Excited, huh? 激动,是吗?


#6楼

According to Apple-doc 根据Apple-doc

  • Weak references are always of an optional type, and automatically become nil when the instance they reference is deallocated. 弱引用始终是可选类型,并且在释放它们引用的实例时自动变为nil。

  • If the captured reference will never become nil, it should always be captured as an unowned reference, rather than a weak reference 如果捕获的引用永远不会为零,则应始终将其捕获为未拥有的引用,而不是弱引用

Example - 范例-

    // if my response can nil use  [weak self]
      resource.request().onComplete { [weak self] response in
      guard let strongSelf = self else {
        return
      }
      let model = strongSelf.updateModel(response)
      strongSelf.updateUI(model)
     }

    // Only use [unowned self] unowned if guarantees that response never nil  
      resource.request().onComplete { [unowned self] response in
      let model = self.updateModel(response)
      self.updateUI(model)
     }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值