弱者自己去哪儿了?

Fat*_*tie 47 weak-references swift

我经常这样做,

let when = DispatchTime.now() + 2.0
DispatchQueue.main.asyncAfter(deadline: when) {
   beep()
}
Run Code Online (Sandbox Code Playgroud)

在一个应用程序中,我们经常这样做

tickle.fresh(){
    msg in
    paint()
}
Run Code Online (Sandbox Code Playgroud)

但如果你做这个

let when = DispatchTime.now() + 2.0
DispatchQueue.main.asyncAfter(deadline: when) {
   tickle.fresh(){
      msg in
      paint()
   }
}
Run Code Online (Sandbox Code Playgroud)

当然,你必须做这个

let when = DispatchTime.now() + 2.0
DispatchQueue.main.asyncAfter(deadline: when) { [weak self] _ in
   tickle.fresh(){
      msg in
      self?.paint()
   }
}
Run Code Online (Sandbox Code Playgroud)

或者,也许这个

let when = DispatchTime.now() + 2.0
DispatchQueue.main.asyncAfter(deadline: when) {
   tickle.fresh(){
      [weak self] msg in
      self?.paint()
   }
}
Run Code Online (Sandbox Code Playgroud)

或许这个

let when = DispatchTime.now() + 2.0
DispatchQueue.main.asyncAfter(deadline: when) { [weak self] _ in
   tickle.fresh(){
      [weak self] msg in
      self?.paint()
   }
}
Run Code Online (Sandbox Code Playgroud)

我们应该做什么?

这三个建议似乎都很完美.这里意义的深度是什么?一个人应该做什么?强引用弱引用,弱引用还是强引用?生存还是毁灭?这就是问题所在!

Ham*_*ish 117

首先,请注意您通常不需要担心保留周期DispatchQueue.main.asyncAfter,因为闭包将在某个时刻执行.因此,无论您是否弱捕获self,您都不会创建永久保留周期(假设tickle.fresh也没有).

是否[weak self]在外部asyncAfter闭包上放置捕获列表完全取决于是否要self在调用闭包之前保留(在设置之后).如果self在调用闭包之前你不需要保持活着[weak self],那么如果你这样做,请放入,然后不要放入.

你是否[weak self]在一个内部封闭物(传递到内部封闭物上tickle.fresh)取决于你是否已经self在外部封闭物中被弱地捕获.如果还没有,那么你可以放置[weak self]以防止内部封闭件保留它.然而,如果外部封闭件已经被弱地捕获self,那么内部封闭件将已经具有弱的参考self,因此[weak self]增加到内部封闭件将不起作用.

所以,总结一下:


DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
   tickle.fresh { msg in
      self.paint()
   }
}
Run Code Online (Sandbox Code Playgroud)

self 将由外封闭和内封闭保留.


DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
   tickle.fresh { msg in
      self?.paint()
   }
}
Run Code Online (Sandbox Code Playgroud)

self 任何一个关闭都不会保留.


DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
   tickle.fresh { [weak self] msg in
      self?.paint()
   }
}
Run Code Online (Sandbox Code Playgroud)

与上述相同,[weak self]内部封闭件的附加物没有效果,因为self外部封闭件已经很弱地捕获.


DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
   tickle.fresh { [weak self] msg in
      self?.paint()
   }
}
Run Code Online (Sandbox Code Playgroud)

self 将由外封闭件保留,但不由内封闭件保留.


当然,可能是您不希望self被外部闭合保留,但您确实希望它由内部闭合保留.在这种情况下,您可以在外部闭包中声明一个局部变量,以便self在您可以在内部闭包中捕获时保持强引用:

DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
   guard let strongSelf = self else { return }
   tickle.fresh { msg in
      strongSelf.paint()
   }
}
Run Code Online (Sandbox Code Playgroud)

现在,self外封闭不会保持活着,但一旦它被调用,如果self仍然存在,它将由内封闭保持活着,直到该封闭被解除分配.


回应:

强引用弱引用,弱引用还是强引用?

弱引用实现为选项,它们是值类型.因此,您无法直接对其进行强引用 - 而是首先必须打开它,然后对底层实例进行强引用.在这种情况下,你只是处理一个强引用(完全像我上面的例子strongSelf).

但是,如果一个弱引用被加框(这会发生在闭包捕获中 - 值类型将被放入堆分配的框中) - 那么你确实可以对该框有一个强引用.这种效果相当于对原始实例的弱引用,你只需要一个不可见的额外间接位.

事实上,这正是在外部闭合弱捕获self并且内部闭合"强烈捕获"弱参考的示例中发生的情况.结果是两个闭合都没有保留self.