简体   繁体   English

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

[英]Why does [weak self] work but [unowned self] break in a Swift closure?

This SpriteKit action repeats by calling itself with a completion closure. 通过完成闭包调用自身来重复此SpriteKit操作。 It uses a closure, rather than an SKAction.repeatActionForever() , because it needs to generate a random variable each repetition: 它使用闭包,而不是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)
  }
}

I think I should be using [unowned self] to avoid a strong reference cycle with the closure. 我想我应该使用[unowned self]来避免与闭包的强引用循环。 When I do that: 当我这样做时:

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

It crashes with the error: _swift_abortRetainUnowned . 它崩溃了错误: _swift_abortRetainUnowned But if I use [weak self] instead: 但如果我使用[weak self]代替:

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

It executes without error. 它执行没有错误。 Why would [weak self] work but [unowned self] break? 为什么[weak self]工作但[unowned self]破裂? Should I even be using either of these here? 我应该在这里使用其中任何一种吗?

The Twinkler object is strongly referenced elsewhere in the program, as a child of another node. Twinkler对象在程序的其他地方被强烈引用,作为另一个节点的子对象。 So I don't understand how the [unowned self] reference is breaking. 所以我不明白[unowned self]引用是如何破坏的。 It shouldn't be deallocated. 它不应该被解除分配。

I tried replicating this problem outside SpriteKit using dispatch_after() , but I was unable to. 我尝试使用dispatch_after()在SpriteKit外部复制此问题,但我无法做到。

If self could be nil in the closure use [weak self] . 如果封闭使用自我可能是零[弱自我]

If self will never be nil in the closure use [unowned self] . 如果自我永远不会在闭包中使用[无主自我]

If it's crashing when you use [unowned self] then self is probably nil at some point in that closure so you would need to use [weak self] instead. 如果你在使用[无主自我]时崩溃,那么在封闭的某个时刻self可能是零,所以你需要使用[弱自我]

The examples from the documentation are pretty good for clarifying using strong , weak , and unowned in closures: 文档中的示例非常适合在闭包中使用strongweakunowned

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

This sounds like a bug. 这听起来像个错误。 {[unowned self] in self.twinkle()} should work identically to {[weak self] in self!.twinkle()} {[unowned self] in self.twinkle()}中的{[weak self] in self!.twinkle()} {[unowned self] in self.twinkle()}应该与{[weak self] in self!.twinkle()}

I had a similar crash recently. 我最近遇到了类似的崩溃。 In my case, sometimes the object get newly initialized happened to have the exact same memory address as the deallocated one. 在我的情况下,有时新对象的初始化恰好与解除分配的对象具有完全相同的内存地址。 The code however, will execute just fine if the two objects have different memory address. 但是,如果两个对象具有不同的内存地址,则代码将执行得很好。

So this is my crazy explanation. 所以这是我疯狂的解释。 When swift put the strong reference to the closure and check its capture list, it checks whether the object has been deallocated or not if the variable in capture list says "unowned". 当swift将强引用置于闭包并检查其捕获列表时,如果捕获列表中的变量显示为“unowned”,它将检查对象是否已被释放。 It doesn't do the check if object is marked as "weak". 如果对象被标记为“弱”,它不会检查。

Since the object is guaranteed never been nil in the closure, it will never actually crash there. 由于保证对象在闭包中永远不会是零,因此它实际上永远不会崩溃。

So, probably a language bug. 所以,可能是语言错误。 And my take on that is use weak rather than unowned. 我对此的看法是使用弱而不是无主。

To not get an error, it should be: 为了不出错,它应该是:

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

not

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

The exclamation after force unwraps which throws an error on nil. 力量解开后的感叹号会在nil上引发错误。 Unowned will throw an error if self is nil just like force unwrapping. 如果self为零,就像强行展开一样,无主将抛出错误。 When doing either of those two options you should use and guard or if statement to protect from nil. 在执行这两个选项中的任何一个时,您应该使用和guard或if语句来保护nil。

This is just my reading of the documentation, but here's a theory. 这只是我对文档的阅读,但这是一个理论。

Like weak references, an unowned reference does not keep a strong hold on the instance it refers to. 与弱引用一样, 无主引用并不会强烈保留它所引用的实例。 Unlike a weak reference, however, an unowned reference is assumed to always have a value. 然而,与弱引用不同,假定无主引用始终具有值。 Because of this, an unowned reference is always defined as a non-optional type. 因此,无主引用始终定义为非可选类型。 [ source ] [ 来源 ]

You said that the Twinkler object is strongly referenced as the child of another node, but children of SKNode are implicitly unwrapped optionals. 你说Twinkler对象被强烈引用为另一个节点的子节点,但SKNode的子节点是隐式解包的选项。 My bet is that the issue isn't that self is being deallocated, but that when you try to create the closure Swift is balking at creating an unowned reference to an optional variable. 我敢打赌,问题不在于self被释放,而是当你试图创建闭包时,Swift正在创建一个对可选变量的无主引用。 As such, [weak self] is the right closure capture list to use here. 因此, [weak self]是这里使用的右关闭捕获列表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM