Memory deallocation in Cocos2d-x

Hi,

I would like to ask You how the objects must be relieved/disposed, how can I destroy unused objects in Cocos2d-x?

1 Like

When should I release the objects?

  • We used C++ to implement NSAutoreleasePool in the engine. It has the same rule with iOS, plz ref to the official document NSAutoreleasePool Class Reference
  • A little difference is that, our NSAutoreleasePool cannot be invoked nested. So there’s only one pool in the entire engine, game deverlopers cannot new any more NSAutoreleasePool objects, but only focus on release/retain the children of cocos2d::NSObject.
  • In one word, there’re only 3 situations you need to call >release method
    **# you “new” an object of cocos2d::NSObject children, such as CCSprite, CCLayer…
    **# you get a pointer of cocos2d::NSObject children, then invoke “retain” once in your code
    **# call the static methods such as CCSomeClass::initXXX. This situation is rarely seen in cocos2d-x
    ** The logic of NSAutoreleasePool is that, when you call object
    >autorelease(), this object is put into the autorelease pool. The pool can help you to retain this object’s lifecycle till the end of current message loop. At the end of current meesage loop, if this object hasn’t retained by other class/container, it will be released automaticly. For example, layer~~>addChild, the sprite is add to the children list of layer, its lifecycle is retained till the release of layer. An example more:
    <pre>
    CCSprite* sprite = CCSprite::spriteWithFile;
    </pre>
    And there’s no any code using “sprite”. Notice that sripte~~>autorelease() is invoked in spriteWithFile, so the sprite is realeased automatically at the end of this meesage loop.

I hope this reply can help you.

1 Like

Hi,

I found that the implemented auto release pool and related classes can not be used in more than two threads. Are you plan to redesign it for multi-threading environment?

Both cocos2d-iphone & cocos2d-x are not designed for multi-thread usage. You had to add locks by yourself.

It’s not only locking. If you look at the implementation, all auto release pools are added to the same stack no matter in which thread they are. This means while one thread is adding objects to the release pool on the top of the stack, the main thread may start to release the objects in the pool. This can not be prevented by using locks with user code. cocos2d-iphone does not suffer from this problem because in cocoa, each thread (including the main thread) maintains its own stack of NSAutoreleasePool object.

oops, ur right. It’s lucky that we haven’t wrap pthread interfaces yet.
We would consider about this. issue #354 is created for this.

Thanks much for Your reply. I don’t understand all the things You have told about releasing the objects.
You wrote that “For example, layer~~>addChild, the sprite is add to the children list of layer, its lifecycle is retained till the release of layer". This means that the sprite object wouldn’t be released when I would call the layer~~>removeChild(sprite, true) method? I think that the if I would set”bool cleanup" parameter to true, the object would be released, and if I would set it to false, it wouldn’t, am I right?
And the other question… is that any method in CCLayer class that I should use to release the objects? You know… I mean - the ‘special’ method, which would be done at the end of the program - is it right to overload the destroy() method in CCLayer class, or should I avoid doing this? When should I write “sprite->release()”, if it should be done only when the program was ended? (the good example may be the “player sprite”, which the player usually see during during the whole game)

@zz
Well, I highlight this sentence in my previous post. And there’s no any code using “sprite”. Ofcourse you can removeChild and release it in your code, but this isn’t in the premise above. When refcount == 0, this object will be delete immediately.

For the 2nd question, just call object->release().Just focus on the refCount of objects, it’s not too hard to trace.

@zz:

>> “is that any method in CCLayer class that I should use to release the objects? You know… I mean - the ‘special’ method, which would be done at the end of the program - is it right to overload the destroy() method in CCLayer class, or should I avoid doing this?”

I’ve found the destroy() method to be a very bad way to release objects. More often than not, the method isn’t even called, even when an object is released through the autorelease pool. I don’t know if this behavior is by design or not, but it sure adds a lot of memory leaks if you’re not aware of it. It’s probably better to ignore destroy() and use the destructor instead.

The system seems to be built with the idea of having everything based on NS/CCObject. If you’re not using CCObject everywhere and you’re mixing it up with custom memory management, things will get iffy. Therefore, initializing at onEnter and deiniting at onExit seems to be the better way if you don’t want to be leaking memory at CCLayer.

(to Walzer and his team: feel free to correct me :slight_smile: I’m just basing my opinion on hours and hours of hunting down memory leaks)

The function destroy is designed for multiple inheritance.
CCLayer inherites CCNode, CCTouchDelegate and others. Because not all platform support RTTI, so CCTouchDelegate can’t inherite CCObject. If CCTouchDelegate inheirte CCObject, there will be a diamond inheritance like this:

             CCObject
                 /  \
                /    \
         CCNode    CCTouchDelegate
             \         /
              \       /
              CCLayer

When we hold a pointer of CCNode that point CCLayer*
CCNode * pNode = new CCLayer();
Than we can not dereference the pointer to CCTouchDelegate*, because the RTTI is not support.

CCTouchDelegate doesn’t inherite CCObject, so it has not methods retain() and release(). How can we keep the refrence correct? So destroy and keep are designed for this purpose. You can see the usage of them in CCTouchHandler::setDelegate().

Random thought: Shall we move m_nTag from CCNode to CCObject, so developers can set tag to each cocos2d-x object, than dump CCAutoreleasePool to see which object isn’t released as expect? Will it be helpful?

So if I type CCLabelBMFont::labelWithString ("hello", FONT);, I still need to call autorelease ()? Cause in my code, I have a LOT of calls like:
@
CCMenuItemLabel *x = CCMenuItemLabel::itemWithLabel (CCLabelBMFont::labelWithString (“Hello!”, FONT), this, menu_selector (Menu::callback));
@

Does this mean I still need to call autorelease on everything?

why do you need to call autorelease()?
the implementation of labelWithString() is as follows:

CCLabelBMFont CCLabelBMFont::labelWithString
{
CCLabelBMFont
pRet = new CCLabelBMFont();
if(pRet && pRet~~>initWithString)
{
pRet~~>autorelease();
return pRet;
}
CC_SAFE_DELETE(pRet)
return NULL;
}

you don’t need to call autorelease() by yourself.

so when do I need to call autorelease? cause in Walzer’s post, he says you call it when you use new, retain, or use a static method to create the object? Does he mean only the static methods that start with init like CCSprite’s initWithSpriteFrameCache?

Hey,
I suggest we move the userdata pointer from CCNode to CCObject so that we can add some dynamic behavior to all CCObject. What do you think?

how do I use the return value of getUserData?

Before you can get anything with getUserData, you need to call setUserData. What you get back with getUserData is what you passed to setUserDate

If we all agree, I will provide a patch for moving the user data point from CCNode to CCObject.

`Cesar de Padua
The rule is the sample as objc NSAutoreleasePool & cocos2d-iphone. When you get an object from static methods just like CCSprite::initWithSpriteFrameCache, this static method set the object to autorelease, so you need to release it unless you call retain once more.

`Leon Li
Sorry, I don’t agree with you. CCObject is only for memory management, and CCNode is the basic node in cocos2d. They have different aims. In other words, there’s no userData pointer in cocoa NSObject.
If you want to add some dynamic behavior to the classes, why not inherit from CCNode instead?

Walzer: sorry, I'm still confused... does that apply to ALL static methods, or only the ones that start withinit@?