3
votes

I've been trying to fix a huge memory increase in SpriteKit for many weeks now but have found no solution. I have tried just about every method that I've come across online for managing memory in SK but am still having troubles. Here's my situation:

I first present the SKScene "HomeScreen" and from there I am changing Page scenes with a UIButton in my View Controller like so:

 func nextPage(sender: UIButton) {

    pageNumber = pageNumber + 1

    let switchPage = SKTransition.crossFadeWithDuration(3.0)
    switchPage.pausesOutgoingScene = false

    switch pageNumber {
    case 1: self.skView.presentScene(Page01(size: (self.view?.bounds.size)!), transition: switchPage)
    case 2: self.skView.presentScene(Page02(size: (self.view?.bounds.size)!), transition: switchPage)
    case 3: self.skView.presentScene(Page03(size: (self.view?.bounds.size)!), transition: switchPage)
    case 4: self.skView.presentScene(Page04(size: (self.view?.bounds.size)!), transition: switchPage)
    case 5: self.skView.presentScene(Page05(size: (self.view?.bounds.size)!), transition: switchPage)
    case 6: self.skView.presentScene(Page06(size: (self.view?.bounds.size)!), transition: switchPage)
    case 7: self.skView.presentScene(Page07(size: (self.view?.bounds.size)!), transition: switchPage)
    case 8: self.skView.presentScene(Page08(size: (self.view?.bounds.size)!), transition: switchPage)
    case 9: self.skView.presentScene(Page09(size: (self.view?.bounds.size)!), transition: switchPage)
    case 10: self.skView.presentScene(Page10(size: (self.view?.bounds.size)!), transition: switchPage)
    case 11: self.skView.presentScene(Page11(size: (self.view?.bounds.size)!), transition: switchPage)
    default:
        break
    }
}

And here is my memory graph from Xcode:

enter image description here

As you can see, my memory for HomeScreen (HS) starts at 38.1 and by the time I have cycled through all the scenes and come back to the Home Screen it ends at 112. I also loaded each page individually and recorded the memory to confirm the increase.

In each SKScene, I load various objects and then in willMoveFromView I am removing them as so:

    override func willMoveFromView(view: SKView) {

    //Remove the Scroller Components
    self.view?.removeGestureRecognizer(self.page01Drops.panGestureRecognizer)
    self.page01Drops = nil

    //Remove Environment
    self.randomObjectFromScene.texture = nil

    self.anotherRandomObject.removeFromParent

    self.removeAllActions()
    self.removeAllChildren()
}

Here's a little more info: - Deinit is not being called after a scene changes.

  • I have no leaks when testing the app with Instruments.

  • All SKTextures are loaded first as UIImages using stringByAppendingPathComponent.

  • All willMoveFromView methods contain removeAllActions and removeAllChildren.

Does anyone know why my memory is increasing?

Update A little more sample code

Here is how I load my skView in viewDidLoad:

    skView = view as! SKView
    skView.showsFPS = true
    skView.showsNodeCount = true
    skView.ignoresSiblingOrder = false
    skView.presentScene(homeScreen(size: skView.bounds.size))

And here is a bit from my Page01 SKScene:

class Page01: SKScene {
    //Preload Textures
    let aaronHead01Texture = SKTexture(image: UIImage(contentsOfFile:NSBundle.mainBundle().resourcePath!.stringByAppendingPathComponent("P01_head01.png"))!)
    let aaronHead02Texture = SKTexture(image: UIImage(contentsOfFile:NSBundle.mainBundle().resourcePath!.stringByAppendingPathComponent("P01_head02.png"))!)
    //more textures for body parts of character that will be changed

    var aaronHead: SKSpriteNode!
    //More SKSpriteNodes for other body parts

    override init(size: CGSize){
        super.init(size: size)

        self.aaronHead = SKSpriteNode(texture: self.aaronHead01Texture)
        self.aaronHead.anchorPoint = CGPoint(x: 0.5, y: 0.1)
        self.aaronHead.position = CGPoint(x: 10, y: 284)
        self.aaronHead.zPosition = 1.0
        self.aaronBody.addChild(aaronHead)

    }
1
As appzYourLife pointed already, it might be that you have a strong reference cycle. Search about terms like unowned self, weak self etc.. Also take a peek at the docs (as best reference) developer.apple.com/library/prerelease/ios/documentation/Swift/…Whirlwind
If you want you can post the properties of some of your scenes as well as the properties of your view controller and I can take a look to see if I can find any obvious retain cycles. The fact that you said deinit is not getting called is proof that you definitely have a retain cycle somewhere in your code. You may also want to make a sample project with deinit working and slowly add your code in until deinit stops working. This is more of a trial and error approach but it will defiant to help you locate the issue.Epic Byte
Thanks Epic Byte, I just added some code. In each of my SKScenes, I create about 12 or so SKSpriteNodes for each of my characters' body parts that are parented together.jwade502
I have found that calling a block that runs self.aaronHead.texture = self.aaronHead02Texture gets retained.jwade502
See developer.apple.com/library/ios/documentation/Swift/Conceptual/… in particular read the section Strong Reference Cycles for Closures which I believe may be causing your issue.Epic Byte

1 Answers

4
votes

You already found the problem:

Deinit is not being called after a scene changes.

If when a scene does change the deinit of the old scene is not called then that's the problem. It means that the old scene is still in memory.

This is probably happening because you have a strong retain cycle. This means that a child (or a child of a child...) of your scene has a strong reference to the scene itself.

You must find that reference and declare it with the weak keyword.