3

Ok so awhile back in testing I had a random crash with no error, and I have no clue why. So i went into analyze things and I came up with the following data. Data

As it would appear my memory usage is getting higher and higher and higher until it sorta plateus. Notice how at the beginning the slope of the general curvature is greater then later on. (as you might notice this is my first time going in and analyzing this sort of thing).

Now what happens in the game is that basically their are two screens. 1. Menu: this screen has quite a few textures but does nothing except has button to play game 2. Game: this has TOOONS of textures and has the bulk of the cpu usage because its the actual game. 3. Death: This screen has one asset, and it is a button that allows you to replay the game. This should not be using much memory OR cpu. However it still has memory. To me this screams whatever a "memory leak" is, is going on.

If you will look at the chart basically what was going on in the game was the menu started, and the first spike was loading up the actual game, then I died. Then from then on I was switching between the Game and Death screens, each spike signals the Game scene being loaded.

If this data were the way I would predict it you would se an oscillation between a very small memory use for the death screen, and then a return to a game memory usage.

Moral of the story is I am pretty sure that sprite kit isn't properly cleaning up after switching scenes and I need to know why if possible.

Btw in order to switch scenes I am using the method made by maxkargin detailed here

BTW I am working in swift with sprite kit and SKScenes, and SKSpriteNodes

Thanks much!

Community
  • 1
  • 1
J.Doe
  • 1,502
  • 13
  • 47
  • 1
    Make sure you stop all SKActions that are running in your game scene before you present the death scene. Can you verify that you've done that? – Ben Kane Aug 23 '15 at 13:55
  • 1
    Invalidate any NSTimers you had running as well if you were using them for some reason. – Ben Kane Aug 23 '15 at 13:57
  • @J.Doe Test to see if dealloc method on each scene is called properly...Maybe you have retain cycle somewhere... – Whirlwind Aug 23 '15 at 14:08
  • @Ben Kane You make it sound like ns timers are a bad thing. I don't use them but are they? – J.Doe Aug 23 '15 at 14:25
  • @whirlwind what would a retain cycle look like in swift? – J.Doe Aug 23 '15 at 14:25
  • @J.Doe they're not bad but you also don't need them in SpriteKit and won't work well with your scene e.g. When you pause the scene an NSTimer keeps running. NSTimer is okay in general it's just notorious for causing memory issues. – Ben Kane Aug 23 '15 at 14:26
  • 1
    @J.Doe what's your status on the SKActions? – Ben Kane Aug 23 '15 at 14:29
  • 1
    @J.Doe About retain cycles ... Read answer posted by LearnCocos2D in this post http://stackoverflow.com/q/19251337/3402095 Those are basics. Search SO about retain cycles if you are interested how you can avoid them, there are a lot of posts on this site about this topic. – Whirlwind Aug 23 '15 at 23:36

4 Answers4

4

There is a few reasons why this is, I had a similar problem with my games. If you do it correctly there is no need to remove stuff such as textures. Removing textures on every scene changes is also not ideal, you want to keep them in memory for performance so they do not have to be reloaded each time.

Here is a basic checklist you can use to see if you create a memory leak.

1) Add the deinit method with a print statement to each scene/class. If deinit gets called your scene deallocated correctly.

 deinit {
    print("Deinit GameScene")
  }

2) Are you creating strong reference cycles somewhere by creating references between 2 classes?

The classic Apple example of a strong reference cycle

 class Person {
    var dog: Dog?
  }

 class Dog {
   var person: Person?
 }

To fix it you would have to make 1 of those 2 properties weak

  class Person {
     var dog: Dog?
  }

  class Dog {
     weak var person: Person?
  }

Also good practice with optionals is to set them to nil when they are no longer needed.

 person = nil

Maybe check google and the Apple Swift documentation on how to deal with this. I also asked a similar question a while back

Swift SpriteKit ARC for dummies

3) Are you using closures? They can cause memory leaks.

A more common scenario in SpriteKit is these 2 examples which could/will cause a memory leak and makes your scene to not deallocate. (action 2 is a closure which captures self)

 // Example 1
 let action1 = SKAction.wait(forDuration: 1)
 let action2 = SKAction.run(someMethod)
 let sequence = SKAction.sequence([action1, action2])
 run(SKAction.repeatForever(sequence))

 // Example 2
 let action1 = SKAction.wait(forDuration: 1)
 let action2 = SKAction.run {
      self.someMethod()
 }
 let sequence = SKAction.sequence([action1, action2])
 run(SKAction.repeatForever(sequence))

A good rule of thumb is that when the compiler forces you to use self than you most likely will create a memory leak without using weak/unowned.

So to fix the above 2 SKAction examples you could either make sure you always remove all actions when you change scenes or IMO even better would be to change your code to this to avoid creating a memory leak in the first place.

 let action1 = SKAction.wait(forDuration: 1)
 let action2 = SKAction.run { [weak self] in
      self?.someClassMethod()
  }
 let sequence = SKAction.sequence([action1, action2])
 run(SKAction.repeatForever(sequence))

Note in all those above example you could also write

 .... { [unowned self] in

and than you would not need to use the ? in the closure

 self.someMethod()

When you use unowned you basically say self will never be nil which could cause a crash if it is actually nil when the closure is called. If you use weak self you tell the compiler self might become nil before the closure is called therefore self is an optional to avoid the crash.

I think it is almost always better to use "weak" instead of "unowned" to avoid this. In one of my games I was using unowned self in a closure that was called when it fetched StoreKit products from iTunes. This caused me subtile crashes because I could exit the SKScene before the closure was called. If you use weak self you will not crash because you use optionals.

Hope this helps a bit

crashoverride777
  • 10,581
  • 2
  • 32
  • 56
  • One small addition to this : removing the `SKAction` will solve the leak as well...Means, no capture list is needed for these particular examples (Example 1 and 2). – Whirlwind Mar 26 '17 at 11:41
  • Yeah thats what I used to do. Its not good practice I think tho, if you forget to do it in each scene you have a leak. I prefer to avoid this by writing more robust code. I will however add your suggestion to my answer. Thanks – crashoverride777 Mar 26 '17 at 11:43
  • Personally, I never do it. I like capture list, but I just mentioned it because it actually solves the issue, and I thought it could be interesting, additional information for the future readers about strong reference cycles issue. – Whirlwind Mar 26 '17 at 11:46
  • Yeah you are right. I added it to my answer. Thanks for the suggestion – crashoverride777 Mar 26 '17 at 11:47
1

sprite kit uses cache to retain perforemce between scenes that why switching between scenes spike up memory and this memory does not release at any case either you remove all children from scene or [self.view presentScene:nil]; their are better solution for this...

read this article carefully

https://developer.apple.com/library/ios/qa/qa1889/_index.html

manage scene with view's and remove SKView's from those view's to maintain memory in multi screen game

Whirlwind
  • 14,286
  • 11
  • 68
  • 157
dragoneye
  • 703
  • 6
  • 14
0

So based on what everyone said this is what I did. Before changing to a new scene I run functions that basically takes all textures, and sprite nodes and sets them to an empty constructor so that they are smaller, then I remove all actions, and children. This has seemed to do the trick! Their might still be other memory problems, however I need to look more carefully and tweak some things before I can be sure.

Thanks for the advice everyone!

J.Doe
  • 1,502
  • 13
  • 47
0

As a side-dish answer, I had a complex scene with a lot of custom sprite kit subclasses and actions interacting together, and the only thing that prevented my SKScene subclass from calling its deinit was one delegate I forgot to define as weak or unowned, which created a classic strong reference cycle problem. So if you assigned any delegates to self in your scene's setup like this:

myCustomNode.delegate = self

Then check the definition of the delegate, it should look like this:

weak var delegate:MyCustomProtocol?

or this:

unowned var delegate:MyCustomProtocol?

If you do not have access to myCustomNode's source code, then try assigning the delegate to nil in your scene like this:

override function willMove(from view:SKView) {
    myCustomNode.delegate = self
}

Also, if you get the error

weak / unowned may only be applied to class and class-bound protocol types

This is because your custom protocol must include : class like this:

protocol MyCustomProtocol : class {/*protocol definition here*/}
mogelbuster
  • 1,066
  • 9
  • 19