I have a simple CCScene containing only one node created from a CocosBuilder template with [CCBReader nodeGraphWithFile:] method.
So far, I did not release the ccb node in the dealloc method of the scene because I expected it to be autoreleased. But in the allocation profiler, I noticed that there is a memory leak if I push/pop the scene several times in the CCDirector.
This memory leak disappears if I actually release the node in the scene's dealloc method.
Why do I need to release the node though I didn't retain/init it ? Is there something I misunderstood ?
What happens to the object created through this?
[CCBReader nodeGraphWithFile:]
If you assign it to a retain property, it will get retained; so you need to release it explicitly. E.g.:
self.nodeGraph = [CCBReader nodeGraphWithFile:...];
if nodeGraph is declared as a retain property, the autoreleased object created in [CCBReader nodeGraphWithFile:] will get retained by the property and you will need to release it in dealloc.
Contrast this to not using a property to keep a reference to the node object and add it directly to the node hierarchy:
[self addChildNode:[CCBReader nodeGraphWithFile:...]];
in this case, you would not need doing any explicit release, since you are not retaining yourself the object.
Related
In the constructor of a QMainWindow...
A new circle is created on the heap and passed to the scene, the circle shows up, everythin is working:
QGraphicsScene * scene = new QGraphicsScene(this);
CustomGraphicsView * view = new CustomGraphicsView(scene,this);
QGraphicsEllipseItem * ellipse = new QGraphicsEllipseItem (100,100,30,30);
ellipse->setPen(QPen(Qt::green,10));
scene->addItem(ellipse);
Now we create circle on the stack and pass it by reference. But this time the cricle will never shows up:
QGraphicsScene * scene = new QGraphicsScene(this);
CustomGraphicsView * view = new CustomGraphicsView(scene,this);
QGraphicsEllipseItem ellipse(100,100,30,30);
ellipse.setPen(QPen(Qt::green,10));
scene->addItem(&ellipse);
I guess it gets destroyed before it shows up. But I don't understand why? Why is this not working and what is the rule behind this behaviour?
From http://doc.qt.io/qt-4.8/qgraphicsscene.html#addItem
Adds or moves the item and all its childen to this scene. This scene takes ownership of the item.
A QGraphicsScene cannot take ownership of an item if it is created on the stack. This implies that the item has to be created on the heap.
QGraphicsEllipseItem * ellipse = new QGraphicsEllipseItem (100,100,30,30);
ellipse->setPen(QPen(Qt::green,10));
scene->addItem(ellipse);
This works because the scene object is in charge of deleteing the object which you've allocated via new. Somewhere in the depths of the Qt framework, there will be a call along the lines of:
delete item;
Now with this code:
QGraphicsEllipseItem ellipse(100,100,30,30);
ellipse.setPen(QPen(Qt::green,10));
scene->addItem(&ellipse);
Consider what happens. The delete item; is still there, but it will be applied not only on a pointer to a local object but (probably, depending on the program flow) also to a pointer to an object which no longer lives. The code which performs the delete simply has no idea that the object was not created via new.
Any attempt to destroy an object more than once is undefined behaviour, as is trying to use an object which was already destroyed. Your question is a good example of why passing around pointers to local objects should be treated as an especially dangerous action which requires extra caution.
By the way:
But this time the cricle will never shows up:
That's a mere coincidence. Undefined behaviour means that everything can happen.
Is there a standard way to change between scenes in Minko? Specifically, I'm imagining each scene as a different level, and when the user completes some task the entire level changes.
I know I could just update all my meshes and whatnot but this feels poor; is there a way I can build a root node for a new scene and then switch the Canvas to using that root node instead (as well as force a rererender, since all the objects will have changed)?
Your second idea is fine. You can create a separate root Node with its own SceneManager sharing the Canvas. Add your new scene to this Node. When you're ready to switch, change the SceneManager you use in the enterFrame signal to render. This should trigger a re-render, upload textures, calls component added signals...
In Minko, there is no global singleton or anything that would prevent from having to completely separate scenes. Each SceneManager will reference its own AssetLibrary. This way, if you switch scenes and remove references to the previous SceneManager, the assets will be released from memory.
Thanx in advance..I have a problem , how we release memory in cocos2dx ??
In my game, my first scene takes a lot of memory because there are so many animations run at a single time on this scene,there are so many animations , so i am satisfied with this but when we go to next scene, it does not release previous memory used,this is my problem so how we release memory used by previous scene when we change the scene?
To go between scenes you can either call pushScene or replaceScene.
Push Scene
If you're pushing between scenes then I recommend that you have a loading scene in between. It should be lightweight so that you have a chance to release everything from your old scene. This is where the onEnter(DidFinish) and onExit(DidStart) methods come in handy.
The chain of calls would be:
oldScene->onExitDidStart()
loadScene->onEnter()
oldScene->onExit() <-- this is where you release everything
loadScene->onEnterDidFinish() <-- this is where you load up the new scene
newScene->onEnter()
and so on... If you've managed your memory correctly then you shouldn't have 2 heavy scenes at once.
Replace Scene
This is a much easier scenario. You simply need to make sure that for the new scene you load as little as possible until the old scene has completely disappeared. I.e. when onEnterDidFinish() is called, or even 1 frame after it.
Cocos2d-x supports auto release memory. For example, when you create a sprite by calling Sprite::create() it will add this sprite to the auto-release pool. If you don't add this sprite to become the child (or n-th leveled child) of the current scene, it will be released.
Do not create an object using new,use create() instead when you call the next scene, everything from the last scene are released.
I'm making an endless running game in which users dodge obstacles, and I'm working on producing the obstacles right now. The plan I had where I'm spawning these obstacles is as follows:
obstacle->setPosition(CCPointMake(this->getContentSize().width, this->getContentSize().height*.75));
obstacle->setScale(.5);
this->addChild(obstacle);
_obstacles->addObject(obstacle);
obstacle->runAction(CCMoveBy::create(2.0, CCPointMake(-(this->getContentSize().width + obstacle->getContentSize().width/2), 0)));
obstacle->removeFromParent();
I set the position, set it's scale, add it to the scene, run an action on it so that it moves across the screen from right to left, add it to an array called _obstacles to be used elsewhere, and then I remove it from the scene so as to save memory. However, the problem is that once I try implement this, the obstacle doesn't show up at all as if it's nowhere to be seen. When I don't call obstacle->removeFromParent() it shows up and performs the action. What am I doing wrong here? If I don't call removeFromParent(), what do I call? Is there a problem in my code not related to removeFromParent()?
The reason that obstacle doesn't apeear at all is that it is removed as it start moving. You just have to create a sequence of move action and function call with obstacle as parameter and than remove this obstacle in that function , so that obstacle will be removed after moving out of screen.
CCCallFuncN *myCallFunc = CCCallFuncN::create(this, callfuncN_selector(CLASS_NAME::removeObstacles));
obstacle->runAction(CCSequence::create(CCMoveBy::create(2.0, CCPointMake(-(this->getContentSize().width + obstacle->getContentSize().width/2), 0)),myCallFunc,NULL));
Method to remove obstacle from array and from parent view
void CLASS_NAME::removeObstacles(CCObject* pSender){
// Type cast pSender to obstacle type e.g if obstacle is of CCSprite type.
CCSprite *tempObstacle = (CCSprite *)pSender;
_obstacle.pop_back(tempObstacle);
tempObstacle->removeFromParent();
}
Don't forget to replace CLASS_NAME by your class name
I would need more info on what removeFromParent is doing and how you use _obstacles
I'm taking a guess on how the parent is structured. I would guess that it has a list of children and when the parent is updated/render, it calls those functions for its children. If removeFromParent removes the object from its parent list, then that would explain why it is not being render or used in the scene. If you have a setup like this, then you should only call removeFromParent before destroying the parent object
I just randomly stumbled upon some answer: https://stackoverflow.com/a/3923131/555690 which states
How memory leakage is solved: [...]
if you switch scene from outside of scheduled selector or there is more than one scheduled
selector then you must unschedule before switching.
Is there any truth to that? I'm skeptical because when a scene is replaced, I'd imagine that it is dealloced, and when that happens it is only logical (to me) that it will automatically unschedule any selector it has. Is there any need to unschedule a selector when you are going to replace the scene?
I'm using cocos2d-iphone 1.0.1.
Just have a look at replaceScene: implementation in CCDirector.m.
You will see it does not do very much: it simply removes your CCScene from an array and replaces it with the new one.
This, if everything works correctly, will make your scene to be deallocated. This will call dealloc both in CCScene and CCNode (CCScene base class). You can also check the implementation and see that those methods do not do anything with scheduled methods.
So, it is up to your CCScene class to do any required cleanup in dealloc (or onExit, whatever makes sense to you).