快速闭合导致自我强烈的保留周期 [英] Swift closures causing strong retain cycle with self

查看:104
本文介绍了快速闭合导致自我强烈的保留周期的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I just want to know if I am understanding this correctly or not. So according to the apple docs when you create a closure as a property of a class instance and that closure references self(The class that created the closure property) this would cause a strong retain cycle an ultimately the class nor the closure will ever get released. So in laymen terms that means that if I have a class that has a property and that property is a closure, and once I assign the functionality of that closure within the class that declares the closure property that will cause a strong retain cycle. Heres a quick example of what I mean

class SomeViewController{
  let myClosure:()->Void

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

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

This ultimately causes a retain cycle since the closure keeps a strong reference to self, which is the class that creates the closure property. Now to fix this according to apple I would define a capture list like so

class SomeViewController{
  let myClosure:()->Void

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

注意如何将[弱自我]放在in语句之前.这使闭包知道仅持有对自身的弱引用,而不持有强引用. IM应当在self可以退出关闭时使用弱点,或者在关闭和self处于相同持续时间时不使用IM.

Notice how I put the [weak self] before the in statement. This lets the closure know to only hold a weak reference to self and not a strong reference. IM supposed to use weak when self can out live the closure or unowned when the closure and self live for the same duration.

我从这里自动引用计数,然后在该链接的强引用封闭循环"部分中,出现语句:如果为类实例的属性分配了一个闭合,并且该捕捉的主体捕获了,则也可能发生强引用循环该实例"那我有这个正确吗?

I got this information from here Automatic Reference Counting and in the Strong Reference Cycles for Closures section of that link, theres this sentence "A strong reference cycle can also occur if you assign a closure to a property of a class instance, and the body of that closure captures the instance" Im about 90% sure Im understanding this correctly but theres just that 10% of doubt. So do I have this correct?

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

The reason why im asking this is because I use callbacks for some of my buttons in my views. And those callbacks call to self but the self in that scenario is the view controller that is responding to the callback not the actual view itself. This is where im doubting myself because I from that sentence I highlighted I don't think I need to put [weak self] on all those button callbacks, but im just making sure. Heres an example of this

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)
     }
 }

推荐答案

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

Yes, that can still cause a retain cycle.

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

The simplest retain cycle is 2 objects that each have strong references to each other, but 3-way and larger retain cycles are also possible.

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

In your case, you have view controller who's view contains a button (a strong reference.) The button has a strong reference to a closure. The closure strongly references the view controller using self. So the view owns the button. The button owns the closure. The closure owns the view controller. If you dismiss the view controller (say it was a modal) then it SHOULD be deallocated. However, since you have this 3-way retain cycle, it won't be deallocated. You should add a deinit method to your view controller with a print statement and try it.

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

The solution is to add a capture list (The [weak self] bit) just like you did in your first example.

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

Note that a common pattern is to add a capture list, and then map the weak variable to a strong variable inside the closure:

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

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

That way, if the closure is still active but the object that owns it was released, the guard statement at the beginning detects that self is nil and exits at the beginning of the closure. Otherwise you have to unwrap the optional every time you refer to it.

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

In certain cases it's also possible that the object in the capture list (self in these examples) could be deallocated in the middle of the closure being executed, which can cause unpredictable behavior. (Gory details: This is only when the closure is run on a different thread than the owner of the object in the capture list, but completion handlers are pretty commonly run on a background thread, so it does happen)

想象一下:

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
}

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

With the code above, if the closure is run on a background thread, its possible that self would still be valid at step 1, but by the time you execute step 2, self has been deallocated. The same goes for steps 3 and 4. By adding the guard strongSelf = self else { return } at the beginning of the closure you test at the entry of the closure to make sure self is still valid, and if it is, you make the closure create a strong reference that only lives as long as the closure takes to run, and that prevents self from being deallocated while the closure code is executing.)

这篇关于快速闭合导致自我强烈的保留周期的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆