弱引用指向哪里?

103

我经常这样做,

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

我们应该怎么做?

所有三个建议似乎都能完美地解决问题。这里的含义是什么?哪一个应该被执行?强引用指向弱引用还是弱引用指向强引用?生存还是毁灭?那是个问题!

1个回答

240

首先要注意的是,通常情况下你不需要担心使用DispatchQueue.main.asyncAfter 时会发生保留循环,因为闭包将在某个时间点运行。因此,无论你是否弱引用self,都不会创建永久的保留循环(假设tickle.fresh也不会创建循环)。

你是否在外部的asyncAfter闭包中加入一个[weak self]取决于你是否想让self被保留直到闭包被调用(即设置的时间后)。如果你不需要self一直存活到闭包被调用,请添加[weak self],否则不要添加。

你是否在内部闭包(传递给tickle.fresh)中加入[weak self]取决于你是否已经在外部闭包中弱引用了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


1
@JoeBlow 这意味着 self 将被外部闭包保持存活,但一旦它被执行,它可能在内部闭包执行之前被释放(假设它是异步执行的)。在最后一个示例中,我使用了带有 guard let 的可选绑定,这将解包弱引用的 self,给我一个强引用 :) - Hamish
1
@JoeBlow 当然可以同时使用两者,但是就像我所说的,如果外部闭包弱引用捕获了 self,那么内部的 [weak self] 就不必要了。 - Hamish
你的留言已经被记录下来了 @JoeBlow :) 顺便说一下,我昨晚很晚才回答了你的问题“强引用指向弱引用是弱引用还是强引用?”不知道你是否看到了。答案是它可以是任何一个 - 取决于你如何将强引用指向弱引用。 - Hamish
1
非常好的答案。 - GoldenJoe
1
非常好的清晰答案,这些ARC内部闭包概念是否有相关文档? - Andrea Gorrieri
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接