我们在Swift中是否总是使用[无主自我]内部关闭

我们在Swift中是否总是使用[无主自我]内部关闭,swift,automatic-ref-counting,Swift,Automatic Ref Counting,在WWDC 2014年第403和403次会议上,出现了以下幻灯片 演讲者说,在这种情况下,如果我们不在那里使用[无主的自我],那将是内存泄漏。这是否意味着我们应该始终在闭包中使用[无主自我] 在上,我不使用[无主自我]。但是我通过使用一些@IBOutlet来更新UI,比如self.temperature和self.loadingIndicator。这可能是正常的,因为我定义的所有@IBOutlet都是弱的。但为了安全起见,我们是否应该始终使用[无主自我] class TempNotifier

在WWDC 2014年第403和403次会议上,出现了以下幻灯片

演讲者说,在这种情况下,如果我们不在那里使用
[无主的自我]
,那将是内存泄漏。这是否意味着我们应该始终在闭包中使用
[无主自我]

在上,我不使用
[无主自我]
。但是我通过使用一些
@IBOutlet
来更新UI,比如
self.temperature
self.loadingIndicator
。这可能是正常的,因为我定义的所有
@IBOutlet
都是
弱的
。但为了安全起见,我们是否应该始终使用
[无主自我]

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

不,有些时候你肯定不想使用
[无主自我]
。有时,您希望闭包捕获self,以确保在调用闭包时它仍然存在

示例:发出异步网络请求 如果您正在发出异步网络请求,则确实希望关闭在请求完成时保留
self
。该对象可能已被释放,但您仍然希望能够处理请求

何时使用无主自我或弱自我 您真正想使用
[unowned self]
[weak self]
的唯一时间是创建一个。强引用循环是指当存在一个所有权循环时,对象最终会相互拥有(可能通过第三方),因此它们永远不会被解除分配,因为它们都确保彼此保持不变

在闭包的特定情况下,您只需要认识到在闭包中引用的任何变量都会被闭包“拥有”。只要闭包存在,这些对象就一定存在。阻止这种所有权的唯一方法是执行
[无主自我]
[弱自我]
。因此,如果一个类拥有一个闭包,并且该闭包捕获了对该类的强引用,那么在闭包和该类之间就有一个强引用循环。这还包括类是否拥有某个拥有闭包的对象

特别是在视频中的示例中 在幻灯片上的示例中,
TempNotifier
通过
onChange
成员变量拥有闭包。如果他们没有将
self
声明为
unowned
,那么闭包也将拥有
self
,从而创建一个强大的引用循环

无主

无主
之间的区别在于
被声明为可选,而
无主
则不是。通过声明它
,您可以处理在某个点闭包内它可能为零的情况。如果您试图访问一个恰好为零的
无主
变量,它将使整个程序崩溃。因此,只有当你为正时才使用
unowned
,如果self在闭包中可能为零,那么当闭包在附近时变量将始终在附近使用[weak self]

如果self在闭包中永远不会为零,请使用[无主self]

Apple Swift文档中有一个很大的部分,其中的图像解释了在闭包中使用strong无主之间的区别:

更新日期:2016年11月 我写了一篇关于这个问题的文章,扩展了这个答案(查看SIL以了解ARC的功能),请查看

原始答案 前面的答案并没有给出明确的规则,说明何时使用一种方法,为什么使用另一种方法,所以让我补充几点

无主或弱讨论归结为变量的生存期和引用它的闭包的问题

情节 您可以有两种可能的情况:

  • 闭包与变量具有相同的生存期,因此闭包只有在变量可访问之前才可访问。变量和闭包具有相同的生存期。在这种情况下,您应该将引用声明为无主。一个常见的例子是
    [unowned self]
    在许多小闭包示例中使用,这些小闭包在其父对象的上下文中执行某些操作,并且没有在其他任何地方被引用都不会比其父对象的寿命长


  • 闭包生存期与变量的生存期无关,当变量不再可访问时,仍然可以引用闭包。在这种情况下,您应该将引用声明为,并在使用它之前验证它不是零(不要强制展开)。一个常见的例子是
    [weak delegate]
    ,您可以在一些引用完全不相关(生命周期)委托对象的闭包示例中看到

  • 实际使用 那么,在大多数情况下,你会/应该实际使用哪一种

    :

    Unowned速度更快,允许不可变和非可选性

    如果你不需要弱者,就不要使用它。

    您将找到更多关于无主
    *
    内部工作的信息


    *
    通常也称为无主(安全),用于指示在访问无主引用之前执行运行时检查(导致无效引用崩溃)。

    以下是对所述详细信息的精彩引用:

    unowned
    vs
    unowned(安全)
    vs
    unowned(不安全)
    unowned(safe)
    是一个非所有权引用,在访问时断言 这个物体还活着。它有点像一个弱的可选参考 这是隐式展开的
    x每次访问它时。
    
    无主(不安全)
    就像
    \uuuuuunsafe\uunretained
    在ARC中,它是一个无主 引用,但没有运行时检查对象是否仍处于活动状态 在访问中,所以悬挂参考
        // 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)
         }
    
    class MyViewController: UIViewController {
          @IBOutlet weak var myButton: UIButton!
          let networkManager = NetworkManager()
          let buttonPressClosure: () -> Void // closure must be held in this class. 
    
          override func viewDidLoad() {
              // use unowned here
              buttonPressClosure = { [unowned self] in
                  self.changeDisplayViewMode() // won't happen after vc closes. 
              }
              // use weak here
              networkManager.fetch(query: query) { [weak self] (results, error) in
                  self?.updateUI() // could be called any time after vc closes
              }
          }
          @IBAction func buttonPress(self: Any) {
             buttonPressClosure()
          }
    
          // rest of class below.
     }
    
    import UIKit
    
    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
    
            let storyboard = UIStoryboard(name: "Main", bundle: nil)
            let controller = storyboard.instantiateViewController(withIdentifier: "AnotherViewController")
            self.navigationController?.pushViewController(controller, animated: true)
    
        }
    
    }
    
    
    
    import UIKit
    class AnotherViewController: UIViewController {
    
        var name : String!
    
        deinit {
            print("Deint AnotherViewController")
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            print(CFGetRetainCount(self))
    
            /*
                When you test please comment out or vice versa
    
             */
    
    //        // Should not use unowned here. Because unowned is used where not deallocated. or gurranted object alive. If you immediate click back button app will crash here. Though there will no retain cycles
    //        clouser(string: "") { [unowned self] (boolValue)  in
    //            self.name = "some"
    //        }
    //
    
    
    //
    //        // There will be a retain cycle. because viewcontroller has a strong refference to this clouser and as well as clouser (self.name) has a strong refferennce to the viewcontroller. Deint AnotherViewController will not print
    //        clouser(string: "") { (boolValue)  in
    //            self.name = "some"
    //        }
    //
    //
    
    
    //        // no retain cycle here. because viewcontroller has a strong refference to this clouser. But clouser (self.name) has a weak refferennce to the viewcontroller. Deint AnotherViewController will  print. As we forcefully made viewcontroller weak so its now optional type. migh be nil. and we added a ? (self?)
    //
    //        clouser(string: "") { [weak self] (boolValue)  in
    //            self?.name = "some"
    //        }
    
    
            // no retain cycle here. because viewcontroller has a strong refference to this clouser. But clouser nos refference to the viewcontroller. Deint AnotherViewController will  print. As we forcefully made viewcontroller weak so its now optional type. migh be nil. and we added a ? (self?)
    
            clouser(string: "") {  (boolValue)  in
                print("some")
                print(CFGetRetainCount(self))
    
            }
    
        }
    
    
        func clouser(string: String, completion: @escaping (Bool) -> ()) {
            // some heavy task
            DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
                completion(true)
            }
    
        }
    
    }