为什么[弱自我]工作但是[无主自我]在Swift闭包中中断?

时间:2014-06-25 17:27:47

标签: closures swift sprite-kit

通过使用完成闭包调用自身来重复此SpriteKit操作。它使用闭包而不是SKAction.repeatActionForever(),因为它需要在每次重复时生成随机变量:

class Twinkler: SKSpriteNode {
  init() {
    super.init(texture:nil, color:UIColor.whiteColor(), size:CGSize(width:10.0, height:10.0))
    twinkle()
  }
  func twinkle() {
      let rand0to1 = CGFloat(arc4random()) / CGFloat(UINT32_MAX)
      let action = SKAction.fadeAlphaTo(rand0to1, duration:0.1)
      let closure = {self.twinkle()}
      runAction(action, completion:closure)
  }
}

我认为我应该使用[unowned self]来避免关闭时的强引用循环。当我这样做时:

let closure = {[unowned self] in self.twinkle()}

它因错误而崩溃:_swift_abortRetainUnowned。但是,如果我使用[weak self]代替:

let closure = {[weak self] in self!.twinkle()}

执行时没有错误。为什么[weak self]可以工作但[unowned self]会中断?我是否应该在这里使用其中任何一个?

Twinkler对象在程序的其他地方被强烈引用,作为另一个节点的子对象。所以我不明白[unowned self]引用是如何破坏的。它不应该被解除分配。

我尝试使用dispatch_after()在SpriteKit外部复制此问题,但我无法做到。

5 个答案:

答案 0 :(得分:17)

如果 self 在闭包中可能为零,请使用 [弱自我]

如果 self 永远不会在闭包中使用 [无主自我]

如果你在使用 [unowned self] 时崩溃,那么在关闭的某个时刻self可能是零,所以你需要使用 [弱自我]

文档中的示例非常适合在闭包中使用无主进行澄清:

https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html

答案 1 :(得分:9)

这听起来像个错误。 {[unowned self] in self.twinkle()}应与{[weak self] in self!.twinkle()}

完全相同

答案 2 :(得分:5)

我最近遇到了类似的崩溃。在我的情况下,有时新对象的初始化恰好与解除分配的对象具有完全相同的内存地址。但是,如果两个对象具有不同的内存地址,则代码将执行得很好。

所以这是我疯狂的解释。当swift将强引用放入闭包并检查其捕获列表时,它会检查对象是否已被释放,如果捕获列表中的变量显示" unowned"。如果对象被标记为"弱"它不会检查。

由于保证对象在闭包中永远不会为零,因此它永远不会在那里崩溃。

所以,可能是语言错误。我对此的看法是使用弱而不是无主。

答案 3 :(得分:2)

为了不出错,应该是:

let closure = {[weak self] in self?.twinkle()}

不是

let closure = {[weak self] in self!.twinkle()}

强行解开后的感叹号会在nil上引发错误。如果self为零,就像强行展开一样,无主将抛出错误。在执行这两个选项中的任何一个时,您应该使用和guard或if语句来保护nil。

答案 4 :(得分:1)

这只是我对文档的阅读,但这是一个理论。

  

与弱引用一样,无主引用不会对它所引用的实例保持强势保持。然而,与弱引用不同,假定无主引用始终具有值。因此,无主引用始终定义为非可选类型。 [source]

您说Twinkler对象被强烈引用为另一个节点的子节点,但SKNode的子节点是隐式解包的选项。我敢打赌,问题不在于self被解除分配,但是当你试图创建闭包时,Swift正在创建一个对可选变量的无主引用。因此,[weak self]是此处使用的右侧闭包捕获列表。