So… on the eve of us getting ready for the iOS 7 Tech Talks, I began having problems with the game on my iPhone 4 device. Basically, every time I died (which is crazy frequent especially since I helped build the game) the game would crash. This didn’t happen on Dan’s phone and I soon found out why…
I was running out of memory.
“Whatever,” thought I. “Dan has like one bajillion gigabytes of RAM on his, we’ll be good until I can figure out what’s causing this.”
Well… today was the day when I decided to help Dan figure it out. I figured we just weren’t releasing something in an object’s destructor and the circular reference was preventing deallocation. Well… yeah… this eventually did prove to be right.
I figured this out by setting a breakpoint on the destructor method and discovering, surprise, surprise, that it was never being called.
Using the debugger, I began watching the reference count Cocos2d-x maintains for each object and watched as that number shot up from three (which I expected) to fifteen (which I did not).
So, now I had to begin watching the m_uReference variable to see where it was being incremented and decremented. I was surprised to find out (though I guess I shouldn’t have been) that it was actually a series of CCSequence objects we’d created for transitions that play through the game and which we’d retained for reuse.
“But,” said I. “That can’t be a problem… we’re releasing it all in the destructor.” Except… it wasn’t being called. And why the hell not?
Well… because that reference count number wasn’t going down so that when the CCDirector replaced the scene, it wasn’t releasing it to be deallocated in the first place.
The solution? Moving all of the release calls on the retained CCSequence‘s out of the destructor and into a new method I literally called deleteReferences(). This then gets called before I set up a new scene to replace the current scene in the game code, releasing the cached sequences and their references to the GameLayer object so it can be properly deallocated.
The thing I’ve found when working with Cocos2d-x is that a fair number of things depend on the order of operations. This is probably because, to some extent, the version of the framework we’re using (2.x), has tried to replicate a fair amount of things that Objective-C has built into the language. This was most likely because the developers wanted to make it as easy as possible to transition games from Cocos2d-iPhone to 2d-x with as few syntax changes as possible. But, with that ease comes a strict adherence to the order in which things must be done.
So… just remember… If you hold onto and strong references of any CCCallFunc actions, just remember that it’s holding a strong reference to the object on which the method is supposed to be called and that these need to be released before your deallocation method or you’ll bleed memory.
This was kind of a tricky memory trap to be caught in, and I don’t know how many other developers will experience this problem, but we worked out the solution and thought we’d go ahead and share it so that others could save time in their development process.