一尘不染

快速闭合导致自我强烈的保留周期

swift

我只是想知道我是否正确理解了这一点。因此,根据苹果公司的文档,当您将闭包作为类实例的属性创建并且闭包引用self(创建闭包属性的类)时,这将导致强大的保留周期,最终该类或闭包都不会被释放。因此,用外行术语来说,这意味着如果我有一个具有属性的类并且该属性是一个闭包,并且一旦我在声明闭包属性的类中分配了该闭包的功能,就会导致强烈的保留周期。这是我的意思的简单例子

class SomeViewController{
  let myClosure:()->Void

  public func someFunction(){
    ....bunch of code
    myClosure = {
       self.dismiss(blahBlahBlah)
    }
  }
}

这最终导致了一个保留周期,因为闭包一直强烈引用self,这是创建闭包属性的类。现在要根据苹果解决此问题,我将定义一个捕获列表,如下所示

class SomeViewController{
  let myClosure:()->Void

  public func someFunction(){
    ....bunch of code
    myClosure = { [weak self] in
       self?.dismiss(blahBlahBlah)
    }
  }
}

注意我如何将[弱自我]放在in语句之前。这使闭包知道仅持有对自身的弱引用而不是强引用。IM应该在自己可以生存的情况下使用弱势,或者在关闭和自我生存的时间相同时不使用IM。

我从“
自动引用计数”和该链接的
“强引用闭环” 部分中获得了此信息,这句话是: “如果将闭包分配给类实例的属性以及主体的主体,也会出现强引用环该关闭捕获了该实例”,
我大约90%的人确信我正确理解了这一点,但只有10%的疑问。那我有这个正确吗?

我问这个的原因是因为我在视图中对某些按钮使用了回调。这些回调调用了self,但是在这种情况下,self是响应回调的视图控制器,而不是实际的视图本身。这是我怀疑自己的地方,因为我从那句话中强调了我,我认为我不需要进行[weak self]所有这些按钮回调,而只是确定一下。这是一个例子

class SomeViewController {
    let someSubview:UIView

    override viewDidLoad() {
       //Some Subview has a button and in that view I just have some action that gets fired off calling the callback here in the view controller I don't need to use the [weak self] in this scenario because closure property is not in this class correct?
       someSubview.someButtonsCallback = {
       ....run code then 
       self?.dismiss(blahBlahBlah)
     }
 }

阅读 183

收藏
2020-07-07

共1个答案

一尘不染

是的,那仍然会导致保留周期。

最简单的保留周期是2个对象,每个对象相互之间都有很强的引用,但是3向和更大的保留周期也是可能的。

在您的情况下,您的视图控制器的视图包含一个按钮(强引用)。该按钮具有对闭包的强引用。闭包使用self强烈引用视图控制器。因此,视图拥有该按钮。该按钮拥有闭包。该闭包拥有视图控制器。如果您关闭视图控制器(例如它是模式控制器),则应该将其释放。但是,由于您具有此3向保留周期,因此不会取消分配它。您应该使用打印语句将deinit方法添加到视图控制器,然后尝试使用它。

解决方案是[weak self]像在第一个示例中那样添加捕获列表(该位)。

请注意,常见的模式是添加捕获列表,然后将弱变量映射到闭包内部的强变量:

let myClosure = { [weak self] in 
  guard let strongSelf = self else { return }
  //...
  strongSelf.doSomething()
}

这样,如果闭包仍处于活动状态,但拥有它的对象已被释放,则开始时的guard语句会检测到self为nil,并在闭包的开头退出。否则,每次引用可选项时都必须将其拆开。

在某些情况下,捕获列表中的对象(在这些示例中为self)也有可能被释放到正在执行的闭包的中间,这可能导致无法预测的行为。(详细信息:仅当闭包在与捕获列表中对象的所有者不同的线程上运行时,但完成处理程序通常在后台线程上运行,因此确实发生)

想象一下:

let myClosure = { [weak self] in

  self?.step1() //1

  //time-consuming code

  self?.property = newValue //2

  //more time-consuming code

  self?.doSomething() //3

  //even more time-consuming code

  self?.doSomethingElse() //4
}

使用上面的代码,如果闭包在后台线程上运行,则可能在第1步中self仍然有效,但是到您执行第2步时,self已被释放。步骤3和步骤4也是如此。通过guard strongSelf = self else { return }在闭包的开头添加,您可以在闭包的入口处进行测试以确保self仍然有效,如果是,则使闭包创建一个仅能生存的强引用。只要闭包需要运行,并且可以防止self在执行闭包代码时将其释放。)

2020-07-07