There are many texts in this article, but these are based on the code written in the previous three chapters, especially the crash when passing values. The expression may differ from the correct mechanism. please correct me. If you have any questions, contact me. You can discuss and learn together.
First, let's clarify the fact that retain and release correspond one by one, just like new and delete.
1. Reference count retain release
Here, please refer to the reference counting books, which must be more detailed than I have said.
To put it simply, add a counter to the new pointer. Each time this memory is referenced, the count is increased by 1. In the destructor, the value is reduced by 1. If it is equal to 0, the delete pointer is left empty.
2. automatically release autolease
The default count of objects after autorelease is 1, and the autorelease objects will be placed in the Auto Release pool. The Auto release pool stores all autorelease objects of the current frame. All objects are release once at the end of the frame, after processing, the release pool no longer has the permission to process these objects. That is to say, the automatic release pool only performs one release operation on the objects in the pool. The release uses a new release pool to store the autorelease object defined in the next frame, so the loop goes on.
After the creation function is executed, the released pool is called at the end of each frame and the memory with reference count 1 is released. If there are no other operations such as retain or addchild, the reference count does not increase. After the end of the current frame, the count is reduced by 1 to 0, and the pointer no longer exists.
When will the Count increase by 1?
Manually call retain to add 1 to the reference technology;
Cocos2dx all the create static methods I have seen call autorelease. The default count is 1.
For example, addChild (), which is the most frequently used, will add 1 to the referenced technology.
When will the count be reduced by 1?
Manually call release to reduce the reference technology by 1;
The automatically released pool will be reduced by 1 at the end of the current frame. Note that the current frame is stored in the subsequent release pool as the autorelease object defined during the runtime of the subsequent frame.
If the structure of a scenario is analyzed, all subnodes are release once, which is called a chain reaction.
The chain reaction is explained as follows: we are currently running this scenario, scenario initialization, adding a lot of layers, there are other layers or genie in the layer, and these are CCNode nodes, taking the scenario as the root, A tree structure is formed. After the scenario is initialized (after one frame), these nodes will be completely attached to the tree structure (internal retain) and are managed by the tree, when we cut a tree branch or root the tree, the "subnode" on it will also be removed (internal through release), which is a chain reaction. From http://www.tairan.com/archives/4184>
Case study:
After create, if retain is not used to add 1 to the reference count, the pool will be automatically released to reduce the reference count by 1. If addchild (sp) is used in the callback function, the system will crash.
To solve this problem, add sp-> retain (); after create to increase its reference count.
As follows:
Auto temp = Sprite: create ("CloseNormal.png"); temp-> retain (); // crash if commented out. Auto item4 = MenuItemLabel: create (Label: createWithBMFont ("fonts/futura-48.fnt", "Hell"), \ [=] (Ref * ref) {addChild (temp );});
Some may use the referenced lambda expression as follows:
auto temp =Sprite::create("CloseNormal.png"); temp->retain(); auto item4 =MenuItemLabel::create(Label::createWithBMFont("fonts/futura-48.fnt","Hell"), \ [&](Ref * ref){ addChild(temp); });
Crashed! The reference will crash even if retain does. Why?
For reference, we use the alias reference of temp, which points to the pointer temp. When this function is executed, temp will be released as a local variable. So the temp we use in the callback function does not exist anymore. If the value is =, the sprite pointer will copy a copy to the lambda expression, so it will not crash.
To solve the issue of reference crash, we only need to make temp not released. So defining it as a member variable can solve the problem caused by referenced lambda expressions. You can try it.
In-depth understanding of CC_SYNTHESIZE_RETAIN
Pretend we have never learned CC_SYNTHESIZE_RETAIN. Article 2 describes the forward transfer between scenarios. If we create an genie in the main scenario and assign the value to the member variable Sprite * sp in the next scenario, how should we perform the value transfer operation for this autorelease variable?
The autorelease variable is deleted by 1 at the end of each frame. Therefore, we should add 1 to the count to avoid deletion in the next scenario.
We should write this when switching the scenario in the main scenario:
VoidMainScene: Morning_0623_MemoryManage (cocos2d: Ref * ref) {auto scene = MemoryManage: createScene (); auto memLayer = (MemoryManage *) scene-> getChildren (). at (0); tmpSp = Sprite: create ("coc/buildings_lowres/59.0.png"); // note the direction of the slash tmpSp-> retain (); // Add 1 to the reference count; otherwise, memLayer> sp = tmpSp will be destroyed at the end of the current frame; // if you do not have a retain, it will be automatically released and will be released when switching the scenario. Director: getInstance ()-> pushScene (scene );}
In the next scenario, MemoryManage should initialize the member variable sp because it is a pointer.
We should define Sprite * sp = nullptr;
Otherwise, it will crash during MainScene replication because an unknown Pointer Points to an unknown area in the memory.
The crash is as follows:
Assert (_ referenceCount> 0, "reference count shocould greater than 0 ");
This is because sp is an unknown pointer.
Next we will discuss the home scene
TmpSp = Sprite: create ("coc/buildings_lowres/59.0.png"); analyze the reference count of the created genie throughout its lifecycle.
Autorelease (1)-> retain (2)-> autorelease Auto release pool release (1)-> addchild (2) -> sub-Scenario Analysis of the chain reaction (1)-> ???
Check whether the count is 1 during the Sub-scenario analysis, which may cause memory leakage. Therefore, we should execute sp-> release () in the destructor. Manually subtract 1.
The emergence of CC_SYNTHESIZE_RETAIN is to solve the above problem. It only wraps the retain and release operations.
At this time, you can go back to the source code of CC_SYNTHESIZE_RETAIN:
#defineCC_SYNTHESIZE_RETAIN(varType, varName, funName) \private: varTypevarName; \public: virtualvarType get##funName(void) const { return varName; } \public: virtual voidset##funName(varType var) \{ \ if (varName != var) \ { \ CC_SAFE_RETAIN(var); \ CC_SAFE_RELEASE(varName); \ varName = var; \ } \}
When CC_SYNTHESIZE_RETAIN is called to assign values to member variables, the original variables will be retaken. Then we need to add the corresponding CC_SAFE_RELEASE (varName) when the Destructor );
Now let's explain why CC_SAFE_RELEASE (varName) is executed for the member variable varName in CC_SYNTHESIZE_RETAIN );
What if varName is assigned multiple times by different variables? Each assignment of the original variable requires a retain operation. If we directly change the varName value without changing the reference count of the memory to which it originally points, this will cause memory leakage. Therefore, each assignment will perform a release for the original memory.
Conclusion: retain and release correspond one by one, but we should use their enhanced version. Macro defines CC_SAFE_RETAIN and CC_SAFE_RELEASE. These two are not exactly the same. For example, the variables defined by CC_SYNTHESIZE_RETAIN only add CC_SAFE_RELEASE to the destructor.