Are there any general guide lines for using retain and release for objects in cocos2d-X ? When creating objects in a function, is it true that the functions memory is cleaned up the second the function returns. When a object is created, calling the retain function of the object, will retain object beyond the function return ?
Kind Regards
Generally in c++ you have this behaviour:
void foo() {
Object a;
Object *pA = new Object();
(…)
}
This would result in a being destroyed automatically at function end, as it was allocated on stack. The *pA would not get destroyed, as it was allocated on the heap (thus, you only loose the reference to it, but the object itself still lives).
Cocos implements a thing called "Automatic Reference Counting" : each CCObject has a reference counter and two methods retain() and release(). The way this works is, that every time you create an object, it gets registered in cocos structers (CCPoolManager). Then with every frame (between them being drawn) there is a maintenance loop which checks the reference counter of all objects : if it is 0 this means (to cocos) that no other objects reference it, so it is safe to delete it. The retain count of an object is automatically incresead when you use this object as an argument for an addChild function.
Example :
void cocosFoo() {
CCSprite *a = CCSprite::create(…);
CCSprite *b = CCSprite::create(…);
this->addChild(b);
}
What happens here is this :
Two CCSprites are created, cocos knows about them.
The b sprite is added to this object (say a CCLayer)
The function ends, no objects are destroyed (both of them being on heap).
Somewhere between this and next frame, the maintanance gets run. Cocos chcecks both sprites and sees that a has reference count == 0, so it deletes it.
This system is quite good, as you don't need to worry about memory management. If you want to create a CCSprite (for example), but not add it as a child yet, you can call retain() on it, which will raise its reference counter, saving it from automatic deletion. But then you'd have to remember about calling release() on it (for example, when adding it as a child).
The general things you have to remeber about are :
Each call to retain() by you needs to be paired with release().
You generally shouldn't delete CCObjects yourself. If you feel that you need to, there is a conveniece macro : CC_SAFE_DELETE(object)
So to answer your questions in short :
Are there any general guide lines for using retain and release for objects in cocos2d-X ?
Yes, you should generally not need to do it.
When creating objects in a function, is it true that the functions memory is cleaned up the second the function returns.
Answer to this is the whole text above.
When a object is created, calling the retain function of the object, will retain object beyond the function return ?
Yes, as will adding it as a child to another (retained in any way) object.
Here is the thing,
cocos2dx has an autorelease pool which drains the objects which have retain count=0 which is a variable to keep in check the scope of the cocos2dx object.
Now when you create new object using the create method it is already added to the autorelease pool and you don't need to release it or delete it anywhere , its like garbage collector in java, takes care of garbage objects behind your back.
But when you create new object using 'new' you definitely need to release it in its destructor or after its use is over.
Second thing,
when your object is added to the autorelease pool but you need it somewhere else you could just retain it , this increments its retain count by one and then you have to manually release it after its use is over.
Third Thing,
Whenever you add child your object it is retained automatically but you don't need to release it rather you remove it from the parent.
Related
I was actually looking for a way to pass by reference in AS3 but then it seemed that adobe and lots of people's understanding of pass by reference is different from what I have been taught at the university. I was taught java was pass by value and C++ allowed pass by reference.
I'm not trying to argue what pass by value and reference are. I just want to explain why I'm using pass by object in the question...
Back to the question, I would like to do something like:
public function swapCard(cardA:Cards, cardB:Cards) {
var temp:Cards = cardA;
cardA = cardB;
cardB = temp;
}
...
swapCard(c1, c2);
EDIT: adding two examples on how I'm using the swapCard function
1) in the process of swaping a card between player1 and player2's hand
swapCard(player1.hand[s], player2.hand[t]);
2) in the process of swaping a card between player1's hand and deck
swapCard(player1.hand[s], player1.deck[rand]);
In C++, we only need to add a symbol before the parameters to make it work (and we call THIS pass by reference). But in AS3, cardA and cardB are just pointers to the formal parameters. Here in the function, changing the pointers does not do anything to the formal parameters :(
I have been searching for hours but I couldn't find a way to without knowing all the properties of the Cards.
If I have to change the properties of the cards one by one then maybe I should change swapCard to a static function in class Cards? (because I don't want to expose everything to another class) I'm not sure if this is a good practice either. This is like adding a swap_cars function into class Cars. If I let this happen, what will be next? Wash car, lend car, rent car... I really want to keep the Cards class clean and holds only the details of the card. Is there a possible way to do this properly in AS3?
The kind of swap function that you're trying to implement is not possible in AS3. The input parameters are references to the input objects but the references themselves are passed by value. This means that inside the function you can change the cardA and cardB but those changes will not be visible outside the function.
Edit: I added this portion after you edited your question with sample usage.
It seems like you're trying to swap two objects in 2 different arrays at given array positions in each - you can create a function for this in AS3 but not the way you attempted.
One possible implementation is to pass the arrays themselves and the positions that you're trying to exchange; something like this:
// Assumes arrays and indices are correct.
public function SwapCards(playerHand:Array, playerCardIndex:int,
playerDeck:Array, playerDeckIndex:int):void
{
var tempCard:Card = playerHand[playerHandIndex];
playerHand[playerHandIndex] = playerDeck[playerDeckIndex];
playerDeck[playerDeckIndex] = tempCard;
}
Note that you still exchange references and the arrays themselves are still passed by reference (and the array references are passed by value - you could, if you wanted, change the arrays to new arrays inside this function but you wouldn't see new arrays outside). However, because the array parameters refer to the same arrays inside and outside the function, you can make changes to the contents of the array (or other array properties) and those changes will be visible outside.
This solution is faster than cloning the card because that involves allocating memory for a new Card instance (which is expensive) and that temporary instance will also have to be freed by the garbage collector (which is also expensive).
You mentioned in a comment that you pass cards down to lower levels of code - if you don't have a back reference to the arrays (and the positions of the cards), you will not be able to easily swap cards - in AS3, all input parameters are copies (either the copy of the value for primitive types or the copy of the reference for complex objects - changes to the input parameters in a function will not be visible outside).
EDIT: renaming the function from clone to copyFrom as pointed out by aaron. Seems like clone is supposed to be used as objA = objB.clone()
At this point, I'm adding a copyFrom() function in the Cards class such that
var temp:Cards = new Cards(...);
var a:Cards = new Cards(...);
...
temp.copyFrom(a);
...
temp will be copying everything from a.
public function swapCard(cardA:Cards, cardB:Cards) {
var temp:Cards = new Cards();
temp.copyFrom(cardA);
cardA.copyFrom(cardB);
cardB.copyFrom(temp);
}
I will wait for a week or so to see if there are any other options
You have some good answers already, but based on the comments back-and-forth with me, here's my suggestion (I use "left" and "right" naming because it helps me visualize, but it doesn't matter):
function swapCard(leftCards:Array, leftCard:Card, rightCards:Array, rightCard:Card):void {
var leftIndex:int = leftCards.indexOf(leftCard);
var rightIndex:int = rightCards.indexOf(rightCard);
leftCards[leftIndex] = rightCard;
rightCards[rightIndex] = leftCard;
}
Now you can swap the cards in the two examples you posted like this:
swapCard(player1.hand, player1.hand[s], player2.hand, player2.hand[t]);
swapCard(player1.hand, player1.hand[s], player1.deck, player1.deck[rand]);
However, note that while this swaps the cards in the arrays, it does not swap direct references to the cards in those arrays. In other words:
var a:Card = player1.hand[0];
var b:Card = player2.hand[0];
swapCard(player1.hand, a, player2.hand, b);
// does not change the references a and b, they still refer to the same card
a == player2.hand[0];
a != player1.hand[0];
b == player1.hand[0];
b != player2.hand[0];
Typically, this sort of thing is handled by dispatching a "changed" event so that any code that cares about the state of a player's hand array will know to re-evaluate the state of the hand.
There's a deep misunderstanding going on here. The question is about object reference but the PO is not trying to swap any Object reference at all.
The problem comes from the fact that the PO does not understand the difference between variable and objects. He's trying to swap variable/object reference which is not dynamically possible of course. He wants with a function to make the variable holding a reference to Object A, swap its object reference with another variable. Since Objects can be passed around but not variables (since they are just holders (not pointers)) the task is not possible without a direct use of the given variable.
To resume:
variables are not Objects!
variables hold a reference to an object.
variables cannot be passed in function or referenced in functions because THEY ARE NOT OBJECTS.
i use cocos2d-x for my game.
i have some question about cocos2d-x create method.
i always use create() method to create new Animation , Animate , RepeatForever Object when the game character needs to change the animation in Update Method
but i think this is not good because if i create new Animation, Animate , RepeatForever Object to change my game character's anitation like below
(below code is just a part of Update method )
auto anim = cocos2d::Animation::create();
anim->setDelayPerUnit(0.3);
anim->addSpriteFrameWithFile("./Hero/walk1_0.png");
anim->addSpriteFrameWithFile("./Hero/walk1_1.png");
anim->addSpriteFrameWithFile("./Hero/walk1_2.png");
anim->addSpriteFrameWithFile("./Hero/walk1_3.png");
auto animation = cocos2d::Animate::create(anim);
m_pBasicSprite->runAction(animation);
when the game character change the animation frequently, then it will cause create too many cocos object. so how should i gonna do?
p.s
i use class memeber variable for save that Object's instead of create new cocos animation object for each loop in update method,
but it cause error the error said "expression _referenceCount > 0"
p.s2
i'm sorry about my terrible english...
Inside ClassName.h:
CCAction* _aniRun;
void createAnimation();
Inside ClassName.cpp:
// create a function
void ClassName::createAnimation()
{
// run ani
CCAnimation* animation=CCAnimation::create();
animation->setDelayPerUnit(0.05f);
for (int i=0; i<1; i++)
{
char str[50];
sprintf(str,"game/bird/run%d.png", i);
animation->addSpriteFrameWithFileName(str);
}
_aniRun = CCRepeatForever::create(CCAnimate::create(animation));
_aniRun->retain();
}
Now wherever you want to play animation just call
player->runAction(aniRun);
Don't forget to release _aniRun inside destructor:
CC_SAFE_RELEASE(_aniRun);
I am also new for cocos2d-x .i do not have full command over it .. i have basic idea about retain() and release().
in cocos 2dx , there is a AutoreleasePool -- which takes care of referancecount , when you call object->autorelease() , this object
will be added to this autoreleasepool. this pool will help you to ratain the object for the lifetime of current frame.
There are 4 functions of Ref, the meanings are
retain: add reference count by 1
release: minus reference count by 1
autorelease: put the object in to AutoReleasePool, which means that cocos2d-x will invoke release automatically at the end of this frame
getReferenceCount: as the meaning of the function, it will retain the reference count of this object
cocos2dx has an autorelease pool which drains the objects which have retain count=0 which is a variable to keep in check the scope of the cocos2dx object.
Now when you create new object using the create method it is already added to the autorelease pool and you don't need to release it or delete it anywhere .
But when you create new object using 'new' you definitely need to release it in its destructor or after its use is over.
when your object is added to the autorelease pool but you need it somewhere else you could just retain it , this increments its retain count by one and then you have to manually release it after its use is over.
Whenever you add child your object it is retained automatically but you don't need to release it rather you remove it from the parent.
There are simple rules with retain() and autorelease():
everything I create with new are mine and I’m responsible for deleting them from memory
everything returned using a create()-method is autoreleased and will be released when the CURRENT METHOD ENDS. If the retainCount is zero, they will be purged from memory. When I need them in an instance variable I have to retain them.
everything I retain I’ll have to release this from memory.
See this retain/release thing as a reference counter. If the counter is down to zero the object will be deleted.
I have these three objects that i need to add on stage, when I extract them from the array on a later stage of the program.
I can't write var mall: Mall = new Mall (); because somebody told me before that, This will add in the object in the memory and will be really bad if a lot of objects are being handled. I will have a lot more objects to work with later on.
So I would like to know a way that when i run a for loop or an event listener i can add ... = new object (); automatically and then call addchild to make an instance of the object.
var mall:Mall;
var library:Library;
var music:Music;
var choices:Array = new Array ();
choices.push(mall,library,music);
test Code for Garbage Collection :
var waking: WakingUp = new WakingUp ();
addChild (waking);
waking.y=-30;
waking.addEventListener (Event.ENTER_FRAME, waking_up);
function waking_up (g:Event){
if (waking.currentFrame == waking.totalFrames)
{
waking.stop ();
waking.removeEventListener (Event.ENTER_FRAME,waking_up);
removeChild (waking);
removeChild(alarm_slide);
gotoAndStop (6);
}
}
This is all the ref I have to this code :D
Specific to instantiating your array with elements, you can:
var choices:Array = [ mall, library, music ];
Flash Player runtime uses both reference counting as well as mark and sweep.
So, any reference to the instance adds a count - collections, display list, event listeners, class members... as instances are dereferenced, the memory will ultimately be freed.
Reference Counting
Each object on the heap keeps track of the number of things pointing to it. Each time you create a reference to an object, the
object's reference count is incremented. When you delete a reference,
the object's reference count is decremented. If the object has a zero
reference count (nothing is pointing to it), it is added to the Zero
Count Table (ZCT). When the ZCT is full, the stack is scanned to find
any references from the stack to an object on the ZCT. Any object on
the ZCT without a stack reference is deleted.
One of the problems of deferred reference counting is circular references. If ObjectA and ObjectB refer to each other but no other
objects in the system point to them, they will never have a zero
reference count and will therefore never be eligible for garbage
collection using reference counting. This is where mark and sweep
garbage collection helps.
Mark/Sweep
Applications that run in Flash Player or AIR have multiple GCRoots.
You can think about a GCRoot as the trunk of a tree with the objects
of the application as the branches. The Stage is a GCRoot. Loaders are
GCRoots. Certain menus are GCRoots. Every object that is still in use
by the application is reachable from one of the GCRoots within the
application. GCRoots are never garbage collected.
Every object in an application has a "mark bit." When the Mark phase
of garbage collection begins, all of those mark bits are cleared. The
MMgc keeps track of all GCRoots in the application. The garbage
collector starts from those roots, traces through each object and sets
the mark bit for every object it reaches. Any object that is no longer
reachable from any of the roots is no longer reachable from anywhere
in the application – its mark bit does not get set during the Mark
phase. Once the collector is done marking all of the objects it finds,
the Sweep phase begins. Any object that doesn't have a set mark bit is
destroyed and its memory reclaimed.
addChild does not make an instance of the object, it simply adds the object to the displaylist of the current display container.
Using new Mall() is fine - just ensure that you remove references to it when it's no longer required so that it may be garbage collected. Alternatively you can just hold an array of primitives.
Riddle me this: I have my MouseEvent.MOUSE_DOWN which calls mouseHandler. The latter looks something like this:
public function mouseHandler(evt:MouseEvent):void
{
var p:Point = new Point(mouseX, mouseY);
var objs:Array = new Array(getObjectsUnderPoint(p));
}
Now what I want to know is thusly: will the objs array and p point be overwritten every time, simply causing the previous objs array and p point to be wiped and a new one generated, or...does it just create a new array and point over and over and over? Of course if I trace(objs) it gives me the expected results, but am I chocking up the system in the background without realising? Your expertise would be appreciated.
EDIT: well after learning a fair bit from the answerers -thanks btw- that made me think of something else and, a quick search later, found a way to theoretically reliably remove the references:
var a = null;
Now I appreciate that this is probably not needed as they'll be GCed at the end of the function, but considering it takes two ticks to write, better safe than sorry, no?
It works like this from what I understand - references for these variables will be lost after function will end. The values will be GC'ed then eventually.
If you will call the function second time, the new references will be created, and filled with new data, the values of previous references hovewer, may still exist in memory, and wait for Garbage Collector, but that may not necessarily happen! If they have attached listeners, the Flash Player will think that they are still useful, so they will exist in the memory to the end of the application run and listen, and even react to events - even if you theoretically cannot access them anymore.
Edit:
In your case whats happening is that you are creating two references that will disappear at the end of the function. They are not related to event listener, the listener creates them, but is not attached to them so it won't stop GC from collecting them after function will end. You creates an array of references, but that are just references to other objects that values are referred in other places (like the display list), if the array alone is not referred anywhere outside of this function, it should be GCted.
Since p is an object, not a primitive, the memory allocation for it will come from the Heap (and therefore need to be garbage collected) instead of the stack like local primitives (and are wiped once the function ends.
So new memory will be allocated every time this function is called, which will temporarily increase memory usage until they get GC'd, but the amount is probably insignificant (assuming that array isn't massive!).
I have a class which is called a number of times. When the application goes to the next stage these all have to be unloaded. Because of that, I created an unload() method in the class.
The problem is that I can't seem to set my uint variable "charId" to null in order to "unset" it. The "delete" command is not possible either as that is only applicable for dynamic variables or something in that kind of way.
Now I wonder, how am I supposed to unset this variable, so it's memory will be re-allocated later on?
The class's unload method:
public function unload():void
{
trace("Unloading character with charname '" + charName + "'.");
enterButton.removeEventListener(MouseEvent.CLICK, enterClicked);
removeChild(enterButton);
enterButton = null;
charName = null;
charId = null; //this is possible but not recommended - what's a better way?
lobbyInterface = null;
}
So yeah, it's practically possible as it changes the variable type - however it's not recommended and raising a warning. So, what's a better way to do it?
Note that this object is also unloaded in it's parent. Does that also free all these variables from memory?
uint, int, Number and Boolean are not nullable in AS3. Number can be NaN, but that is really the best you can get. int and uint are always just 32 bit, so you can't stuff a null-reference in there.
The type of cleanup you are trying to do cannot be accomplished since AS3 has the concept of sealed classes. A sealed class has a fixed size in memory. When it comes to instance variables, think of it as a C struct, you can only dump all of it, or nothing. You can do anything in C of course, it's a fixed block in memory, an entity of one reference per variable.
What you want to do is only work with dynamic variables, which are maintained differently.
You don't need to do this sort of cleanup since Flash has garbage collection like most runtimes nowadays. It also deals with nested and circular references, the only thing you have to be sure about is, that you delete any "outer" references to that class. Things that are generally not collected are objects on the display list, running timers and intervals, and I/O related stuff. As soon as you have a reference chain from there to your object, it will not be collected.
Let us say you have an object A with an event handler for a mouse movement on an object on some list, referencing an object B. B will not be collected, but as soon as there is no chain leading to an object, it will be collected (sooner or later, the GC is quite lazy. But the more memory you use, the more it does its work).