【问题标题】:Where does the weak self go?弱小的自己去哪了?
【发布时间】:2017-06-18 21:21:30
【问题描述】:

我经常这样做,

let when = DispatchTime.now() + 2.0
DispatchQueue.main.asyncAfter(deadline: when) {
   beep()
}

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

tickle.fresh(){
    msg in
    paint()
}

但是如果你这样做这个

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

当然你必须这样做这个

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

或者,也许这个

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

或者这个

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

我们应该怎么做?

所有三个建议似乎都可以完美运行。这里的全部含义是什么?应该做什么?强引用是弱引用、弱引用还是强引用?生存还是毁灭?就是这个问题!

【问题讨论】:

    标签: swift weak-references


    【解决方案1】:

    首先,请注意,您通常无需担心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()
       }
    }
    

    self 将被外部和内部闭包保留。


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

    self 不会被任一闭包保留。


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

    同上,内闭包的附加[weak self] 无效,因为self 已被外闭包弱捕获。


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

    self 将被外部闭包保留,但不会被内部闭包保留。


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

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

    现在,self 不会被外部闭包保持活动状态,但是一旦调用它,如果 self 仍然存在,它将被内部闭包保持活动状态,直到该闭包被释放。


    回应:

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

    弱引用被实现为可选项,它们是值类型。因此,您不能直接对一个实例进行强引用——相反,您首先必须解包它,然后对底层实例进行强引用。在这种情况下,您只是在处理一个强引用(就像我上面使用 strongSelf 的示例一样)。

    然而,如果一个弱引用被装箱(这发生在闭包捕获中——值类型将被放入一个堆分配的盒子中)——那么你确实可以对该盒子有一个强引用.这样做的效果相当于对原始实例的弱引用,你只是有一个不可见的额外间接。

    事实上,这正是在外部闭包弱捕获self 而内部闭包“强烈捕获”该弱引用的示例中发生的情况。效果是两个闭包都不会保留self

    【讨论】:

    • @JoeBlow 这意味着self 将被外部闭包保持活动状态,但是一旦它被执行,它可以在内部闭包被执行之前被释放(假设它是异步执行的)。在最后一个示例中,我使用与guard let 的可选绑定,它解开弱self,给我一个强烈的参考:)
    • @JoeBlow 当然,同时使用两者并没有什么坏处——尽管正如我所说,如果外部闭包弱捕获 self,则内部 [weak self] 是不必要的。
    • 您的注释已被注意到@JoeBlow :) 我添加了对您的问题“强引用是弱引用、弱引用还是强引用?”的回复。顺便说一句,不知道你有没有看到。答案是两者都可以——这取决于你如何将强引用变为弱引用。
    • 非常好的答案。
    • 非常好的和明确的答案,是否有与这些 ARC 内部闭包概念相关的文档?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-18
    • 1970-01-01
    • 2011-03-03
    • 2014-03-15
    • 2015-07-17
    相关资源
    最近更新 更多