5

I've got a great problem and I don't understand very well why occurs. This is the case:

  • Have a great project in Cocos2D with 10 scenes. Each scenes is a page of a book with huge sprites. It uses Kobold2D 1.0.2 implementation.
  • Every page has common objects in a singleton class, to put a common menus via LayerColor.
  • The sprites is TexturePacker in PVR.CCZ RGBA4444 and in iPad memory are around 16-20Mb every spritesheet loaded.
  • I use CCTransitionTurnPage for replaceScene for one to the next one.
  • At init method of each page (class), load the texture and FrameFile.
  • At onExit method of each page (class), unload the textures and frameFile. I used the dumpCachedTextureInfo and says me textures load and unload from memory perfectly.
  • Of course, I remove from child all objects. All my sprites are common variables declared on the interface section at .h, because I need to access them in every method of the class.
  • My project is made with Kobold2D integration in a ARC project (but you know the attached Kobold2D project has ARC not enabled for compatibility problem)

The fact is when I start the project, everything seems perfect but memory is increasing for every scenes (page) I made. Page1: 30Mb., Page2: 40, Page 3: 54, Page 4: 65... After 7 or 8 scenes, Instruments, Xcode or the iPad itself hangs the application without any message (except Instruments with a final Low memory Warning).

Why there's no memory releasing after each scenes? Maybe it's because ARC and the no super dealloc variable. Why textures appears unload perfectly but seems like there's no unload because memory is growing without control until crash?

skolima
  • 31,963
  • 27
  • 115
  • 151
  • Set a breakpoint in the dealloc method of a scene, if dealloc is not called you have a retain cycle (ARC can't prevent that, see also: http://stackoverflow.com/questions/3634435/can-any-one-explain-retain-cycle-with-example-codeobjective-c-and-how-can-we). In particular check that none of the CCNode* properties have strong or weak attributes, they should be set to assign. – CodeSmile Feb 11 '12 at 18:28
  • 2
    Finally I must to disabled ARC, because of this retain cycle on the scene. I cannot find any solution to do a real dealloc of each scene and I must to disabled ARC. When I do it, everything goes well. RemoveAllTextures now freeze memory (with ARC does not) and super dealloc work perfectly. When I had ARC enabled override dealloc not fire, and I used onExit to unload textures and freeze memory, but doesn't work. Thanks a lot for your help! – Julio César Fernández Muñoz Feb 14 '12 at 11:45
  • 2
    Most likely disabling ARC only changed the way your code bugs behave, in particular if you had a circular reference ARC or no ARC should make no difference. It's possible that you're now leaking objects silently. – CodeSmile Feb 16 '12 at 23:37
  • 1
    Honestly... Like Judge Dredd said: "I knew you'd say that!"... And I think you're right. I think dealloc it's only a patch because app grows until 80Mb in the last scene. How can I detect this cycles or leaks... because Instruments doesn't show me anything about and the retainCount of the textures is more than 20 in some cases. Thanks a lot for your help! – Julio César Fernández Muñoz Feb 17 '12 at 11:42

2 Answers2

10

I was having a similar problem with memory retention and no leaks showing up in instruments. I couldn't get -(void) dealloc to even get called until I wrote the following into every scene's .m file:

-(void) onExit {
    //unschedule selectors to get dealloc to fire off
    [self unscheduleAllSelectors];
    //remove all textures to free up additional memory. Textures get retained even if the sprite gets released and it doesn't show as a leak. This was my big memory saver
    [[CCTextureCache sharedTextureCache] removeAllTextures];
    [super onExit];
}

After implementing this, my memory was released after every replaceScene: was called. Hope this is of some use to you.

BobbyScon
  • 2,537
  • 2
  • 23
  • 32
  • 2
    I always used [[CCTextureCache sharedTextureCache] removeUnusedTextures]; thinking that Cocos2D would figure out that the texture is indeed unused and thus remove it. Turns out it cannot determine it reliably, so removeAllTextures, albeit slower, is better to prevent memory issues. – axello Jun 06 '12 at 11:16
3

After months of working, I learned the most important lesson on Cocos2D. The retain increase by one when you put any CCNode object on a CCArray or NSArray or NSDictionary... This means you must release the objects from this objects before the CCLayer or CCScene dealloc.

You must put a [array removeAllObjects] or [dictionary release] on the - (void) cleanup and after all your objects has removed, then put a [super cleanup];

In the meantime, on - (void) onExit you must remove all scheduler from the instance. Not only a scheduler itself. Remember to stopAllActions on any CCNode. But, carefully, because stoping actions from CCNodes (Sprites or something) must be on cleanup, before any removeAllObjects.

And remember: if CCLayer or CCScene does not remove properly, SimpleAudioEngine not will release the audio too.

Mogsdad
  • 44,709
  • 21
  • 151
  • 275