Using unowned inside of a capture list causing a crash even the block itself isn't executed

社会主义新天地 提交于 2019-11-30 20:04:09

the crash is happening because the GameScene object has been released before the animation finishes.

one way to implement this would be to pass the SomeClass object back in the closure.

class SomeClass {
    var closure: (SomeClass->Void)?
}

which would be used like this:

override func didMoveToView(view: SKView) {
    let someInstance = SomeClass()
    someInstance.closure = { [unowned self] someClass in
        someClass.method() // no more retain cycle
        self.method()
    }
}

UPDATE

turns out this is a combination of a nuance in the convenience init?(fileNamed:) + misuse of [unowned self] causing your crash.

although the official documentation doesn't seem to state it, as briefly explained in this blog post the convenience initializer will actually reuse the same object.

File References

The scene editor allows you to reference content between different .sks (scene) files, meaning you can put together a bunch of sprites in a single scene file and then reference the file from another scene file.

You might wonder why you would need more than one scene, and there a couple of reasons:

1) You can reuse the same collection of sprites in multiple different scenes, meaning you don’t have to recreate them over and over again.

2) If you need to change the referenced content in all of your scenes, all you have to do is edit the original scene and the content automatically updates in every scene that references it. Smart, right?

adding logging around the creation, setting of the closure and deinit leads to some interesting output:

GameScene init: 0x00007fe51ed023d0
GameScene setting closure: 0x00007fe51ed023d0
MenuScene deinited
GameScene deinited: 0x00007fe51ed023d0
GameScene init: 0x00007fe51ed023d0
GameScene setting closure: 0x00007fe51ed023d0

notice how the same memory address is used twice. i'd assume under the hood apple is doing some interesting memory management for optimization which is potentially leading to the stale closure still existing after deinit.

deeper digging could be done into the internals of SpriteKit, but for now i'd just replace [unowned self] with [weak self].

From what I can see if seems like the retain cycle would be caused because you are including an object in its own closure and saving it to itself. See if the following works:

class GameScene: SKScene {

    let someInstance = SomeClass()

    override func didMoveToView(view: SKView) {

        backgroundColor = .blackColor()

        let closure = {[weak self, weak someInstance] in

            someInstance?.method() //This causes the strong reference cycle...
            self?.method()  
        }
        someInstance.closure = closure
    }

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {

        if let nextScene = MenuScene(fileNamed: "MenuScene"){
            nextScene.scaleMode = .AspectFill
            let transition = SKTransition.fadeWithDuration(1)
            view?.presentScene(nextScene, transition: transition)
        }
    }

    deinit {print("GameScene deinited")}

    func method(){}
} 

I moved someInstance to a property of the class because I am pretty sure with a weak reference in the block and without passing someInstance somewhere outside the function, someInstance will deinit at the end of that function. If thats what you want then keep it someInstance inside the function. You can also use unowned if you want but as you know I guess I am just a bigger fan of using weak lol. Let me know if that fixes the leak and crash

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!