iOS 7 Sprite Kit Freeing Up Memory

Sprite kit, remove animated textures from memory

SpriteKit utilizes its own caching mechanism and there is no way for you to clear out the cache yourself.

From what you said, it appears that you have a memory leak somewhere in your code. Keep in mind that you need to remove all references for a texture to get deallocated by ARC.

Removing a texture from parent is not enough in your case as the textures are stored in an array. You would need to remove all textures from the array and have no other references to them in order to properly release them.

450MB+ is absolutely too much for just a couple of textures. I suggest you run Instruments and check for memory leaks.

removing SCNNode does not free memory before creating new SCNNode

I had the same problem, with my SceneKit app leaking a lot of memory. The memory of the SCNNode is freed if you set its geometry property to nil before letting Swift deinitialize it.

Here is an example of how you may implement it:

class ViewController: UIViewController {
@IBOutlet weak var sceneView: SCNView!
var scene: SCNScene!

// ...

override func viewDidLoad() {
super.viewDidLoad()
scene = SCNScene()
sceneView.scene = scene

// ...
}

deinit {
scene.rootNode.cleanup()
}

// ...
}

extension SCNNode {
func cleanup() {
for child in childNodes {
child.cleanup()
}
geometry = nil
}
}

Preloading sprite kit textures

The thing is, do you really want to cache the resources like that? Can't say I ever found a need for something of that nature. Anyways, if doing that somehow helps with your app's performance, then you can make a TextureManager class which would be a singleton (create separate file for TextureManager class), like this:

class TextureManager{

private var textures = [String:SKTexture]()

static let sharedInstance = TextureManager()

private init(){}


func getTexture(withName name:String)->SKTexture?{ return textures[name] }

func addTexture(withName name:String, texture :SKTexture){


if textures[name] == nil {
textures[name] = texture
}
}

func addTextures(texturesDictionary:[String:SKTexture]) {

for (name, texture) in texturesDictionary {

addTexture(withName: name, texture: texture)
}
}

func removeTexture(withName name:String)->Bool {

if textures[name] != nil {
textures[name] = nil
return true
}
return false
}
}

Here you are using dictionary and associate each texture with its name. Pretty simple concept. If there isn't a texture with the same name in a dictionary, then add it. Just beware of premature optimization.

The usage:

 //I used didMoveToView in this example, but more appropriate would be to use something before this method is called, like viewDidLoad, or doing this inside off app delegate.
override func didMoveToView(view: SKView) {

let atlas = SKTextureAtlas(named: "game")

let texture = atlas.textureNamed("someTexture1")

let dictionary = [
"someTexture2": atlas.textureNamed("someTexture2"),
"someTexture3": atlas.textureNamed("someTexture3"),
"someTexture4": atlas.textureNamed("someTexture4"),

]

TextureManager.sharedInstance.addTexture(withName: "someTexture", texture: texture)
TextureManager.sharedInstance.addTextures(dictionary)

}

As I said, you have to put TextureManager implementation in a separate file, to make it real singleton. Otherwise, if you define it in GameScene for example, you will be able to call that private init, and then TextureManager will not be a real singleton.

So, with this code you can create some textures at the very beginning of the app lifecycle, like it is said in the docs:

For example, if your game uses the same textures for all its gameplay,
you might create a special loading class that runs once at startup.

and fill the dictionary with them. Later on, whenever you need a texture, you will not use atlas.textureNamed() method, but rather load it from a dictionary property of a TextureManager class. Also, when transitioning between scenes, that dictionary will survive scene's deinits, and will persist while app is alive.



Related Topics



Leave a reply



Submit