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

时间:2018-05-09 20:11:37

标签: swift closures retain-cycle

我只是想知道我是否正确理解这一点。因此,根据apple docs,当你创建一个闭包作为类实例的属性并且该闭包引用self(创建闭包属性的类)时,这将导致一个强大的保留周期,最终类和闭包将被释放。所以在非专业术语中,这意味着如果我有一个具有属性且该属性是闭包的类,并且一旦我在类中分配了该闭包的功能,该类声明了将导致强保留周期的闭包属性。下面简要说明我的意思

class SomeViewController{
  let myClosure:()->Void

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

这最终导致一个保留周期,因为闭包保持对self的强引用,self是创建闭包属性的类。现在根据苹果来解决这个问题我会定义一个像这样的捕获列表

class SomeViewController{
  let myClosure:()->Void

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

注意我如何在in语句之前放置[weak self]。这使得闭包只知道对自身的弱引用而不是强引用。当封闭和自我生活持续相同的持续时间时,IM应该使用弱的时候自我可以关闭或无主。

我从这里Automatic Reference Counting和该链接的强引用循环部分获得了这些信息,这句话“如果你为一个属性分配一个闭包,也会出现一个强引用循环一个类实例,该闭包的主体捕获实例“我约90%确定我正确理解这一点,但只有10%的怀疑。我有这个正确吗?

我问这个的原因是因为我在我的视图中使用了一些回调。那些回调调用自己,但那个场景中的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)
     }
 }

1 个答案:

答案 0 :(得分:8)

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

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

在您的情况下,您有视图控制器,其视图包含一个按钮(强引用)。该按钮具有对闭包的强引用。闭包强烈引用使用self的视图控制器。所以视图拥有按钮。按钮拥有关闭。该闭包拥有视图控制器。如果你关闭视图控制器(说它是模态),那么它应该被解除分配。但是,由于您具有此3向保留周期,因此不会取消分配。您应该使用print语句向视图控制器添加一个deinit方法并尝试它。

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

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

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

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

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

想象一下:

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被释放。)