Swift 3 (SpriteKit): Reseting GameScene doesn't deallocate

与世无争的帅哥 提交于 2019-12-04 21:12:38

About presentScene(SKScene?) method

When you call this method, based on what you are passing to it, different things will happen...But first things first... Before scene is presented by an SKView, you have a newly created scene and an SKView instance. SKView instance has scene property, and a scene has a view property. These properties are set accordingly and automatically (scene becomes aware of its view, and view becomes aware of newly created scene) when scene is presented.

Now when you call presentScene(nil), the current scene is being removed from a view, so automatically its view property becomes nil (also scene property on an SKView instance becomes nil).

The Gray Screen

Because the scene has been removed from a view, you see the gray screen now.That is default color of a view. So, no scene currently. And you are going to say, but how? I did:

self.view?.presentScene(newScene, transition: animation)

Well that code didn't crash because of optional chaining. Everything has failed gracefully (line wasn't executed). But now you are going to say:

"Wait, I've seen deinit call!"

That's true. But what you have seen is a deinit call for the newScene instance. That is because the line above failed, and the new scene is not presented, and the current method reached the end, and because nothing retaining this new instance, it deallocates. Still, the current scene is not deallocated actually, because if that was the case, you would see two deinit calls.

Here is how you can check which instance has been deallocated (check memory addresses):

 deinit{
      print("deinit : \(self) has address: \(Unmanaged.passUnretained(self).toOpaque())")
    }

If you print out self.view after self.presentScene(nil) line, you will see that self.view is nil. And because you are using optional chaining, everything fails gracefully. If you change self.view!.presentScene(newScene) you will have a crash yelling about finding a nil while unwrapping the optional.

Calling presentScene(nil) will not resolve leaks

Calling this method, would just set scene property to nil on an SKView that presents it. It will not deallocate the scene if something retaining it. Say you did some silliness in your GameViewController, and made a strong reference to the scene that is currently presented (I guess no code needed for this?) so the scene will be retained until GameViewController deallocates...Means, when you do:

presentScene(nil) 

nothing will happen. scene's deinit will not be called. Remember, this method just set scene property to nil on an SKView that presents the scene, but if that is not a last strong reference to the currently disposing scene, the scene wont be deallocated (that is how ARC works).

Also, you could have multiple leaks. It is not the scene which leaks. You can have other objects leaking, eg. object A is a property of scene, and object B is a property of a scene. Object A points strongly to Object B, and vice versa. That is strong reference cycle. Now scene will deallocate, but these two objects will stay in memory.

That is why I proposed in comments to override deinit methos on a custom classes.

How to solve a leak?

I would skip going into detail about that because that would be just copy/pasting Apple's documentation and there is a great explanation of how to solve/prevent leaks (Person & Apartment example). But the point is, use weak/unowned references inside of a classes that might retain eachother. Same goes for closures. Define capture lists and use weak/unowned self in the case that scene retains the block, and block retains the scene.

For those interested more into presentScene(SKScene?) method, there are some quotes from a Technical Note that I've linked below (I wonder why info like this is not a part of docs for presentScene(SKScene?) method).

There is a mention in Technical Note that says:

SKView maintains a cache of the scenes it presents for performance reasons

and

Passing a nil argument to SKView presentScene does not cause the scene to be freed, instead, to release the scene, you must free the SKView that presented it.

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