为什么在Swift闭包中[weak self]可以工作,而[un主self]却中断了?



这个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外复制此问题,但我无法。

如果self在闭包中可以为nil,则使用[weak self]

如果self在闭包中永远不会为nil,则使用[un主self]

如果当你使用[un主self]时它崩溃了,那么self在闭包中的某个点可能是nil,所以你需要使用[weak self]

文档中的示例非常好地说明了在闭包中使用无主:

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

这听起来像是一个bug。{[unowned self] in self.twinkle()}应该和{[weak self] in self!.twinkle()}一样

我最近也遇到过类似的崩溃。在我的例子中,有时刚刚初始化的对象恰好与已释放的对象具有完全相同的内存地址。然而,如果两个对象具有不同的内存地址,代码将执行得很好。

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

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

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

为了不出现错误,它应该是:

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

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

force unwrap后的感叹号,会抛出nil错误。如果self为nil就会抛出错误,就像强制展开一样。当做这两个选项中的任何一个时,你应该使用and guard或if语句来防止nil。

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

与弱引用一样,无主引用不会对它所引用的实例保持强控制。但是,与弱引用不同,假定无主引用总是有一个值。因此,无主引用总是定义为非可选类型。[源]

您说Twinkler对象是作为另一个节点的子对象强引用的,但是SKNode的子对象是隐式打开的可选对象。我敢打赌,问题不在于self正在被释放,而是当你试图创建闭包时,Swift在创建一个对可选变量的无主引用时犹豫了。因此,[weak self]是此处使用的正确闭包捕获列表。

最新更新