[Glossary]
Reference counting: reference counting is a concept frequently used in modern memory management. Its basic idea is to reference a shared object at the same time by counting multiple different objects. Specifically, when an instance of an object is created and memory is allocated on the stack, the reference count of the object is 1. When other objects need to hold this shared object, add the reference count of the shared object to 1. When other objects no longer hold the shared object, the reference count of the shared object is reduced by 1. When the reference count of the shared object is changed to 0, the memory of the object is immediately released. (Some of them are taken from Wikipedia ).
COM and objective-C are well-known to use reference counting. In the iunknow interface of COM, three functions are defined: QueryInterface, addref, and release, they are used to obtain interface objects, increase the count for interface objects, and reduce the count for interface objects. When the internal count is 0, the interface objects are automatically destroyed. In objective-C, the retain, release, and autorelease functions are defined to increase or decrease the count, and an object is handed over to the autoreleasepool of the automatically released pool object for management, the autoreleasepool object calls the release function.
[Ref class implementation]
Since Cocos2d-x is developed on the basis of Cocos2d-iPhone, so follow a lot of objective-C ideas, the implementation of ref class is so.
The ref class implements the reference counting function. It is the parent class of the vast majority of other classes in Engine Code and is defined in ccref. h and implemented in ccref. cpp. In fact, the ccref. h file not only defines the ref class, but also defines the clonable class and a series of macro definitions and Type Definitions. However, we will focus on the interpretation of the ref class for the moment. The ref class uses the private member Variable _ referencecount to save the Count value, and uses the retain, release, and autorelease functions to increase or decrease the Count value.
<span style="font-family:Microsoft YaHei;font-size:18px;">class CC_DLL Ref{public: /** * Retains the ownership. * * This increases the Ref's reference count. * * @see release, autorelease * @js NA */ void retain(); /** * Releases the ownership immediately. * * This decrements the Ref's reference count. * * If the reference count reaches 0 after the descrement, this Ref is * destructed. * * @see retain, autorelease * @js NA */ void release(); /** * Releases the ownership sometime soon automatically. * * This descrements the Ref's reference count at the end of current * autorelease pool block. * * If the reference count reaches 0 after the descrement, this Ref is * destructed. * * @returns The Ref itself. * * @see AutoreleasePool, retain, release * @js NA * @lua NA */ Ref* autorelease(); /** * Returns the Ref's current reference count. * * @returns The Ref's reference count. * @js NA */ unsigned int getReferenceCount() const;protected: /** * Constructor * * The Ref's reference count is 1 after construction. * @js NA */ Ref();public: /** * @js NA * @lua NA */ virtual ~Ref();protected: /// count of references unsigned int _referenceCount; friend class AutoreleasePool;};</span>
Ref declares the constructor as a protection type to prevent the ref object from being generated directly. In the constructor's member initialization list, the reference count value _ referencecount is initialized to 1. The retain function adds _ referencecount to 1, the release function minus 1, and the autorelease function hosts the object to the autoreleasepool object for management. The specific implementation code is as follows:
<span style="font-family:Microsoft YaHei;font-size:18px;">NS_CC_BEGINRef::Ref() : _referenceCount(1) // when the Ref is created, the reference count of it is 1{}Ref::~Ref(){}void Ref::retain(){ CCASSERT(_referenceCount > 0, "reference count should greater than 0"); ++_referenceCount;}void Ref::release(){ CCASSERT(_referenceCount > 0, "reference count should greater than 0"); --_referenceCount; if (_referenceCount == 0) { delete this; }}Ref* Ref::autorelease(){ PoolManager::getInstance()->getCurrentPool()->addObject(this); return this;}unsigned int Ref::getReferenceCount() const{ return _referenceCount;}NS_CC_END</span>
[Definition of clonable class]
The clonable class defines the conventions for copying ref class objects. It is similar to the java. Lang. clonable interface in Java. We recommend using the clone function in version 3.2. The copy function is an obsolete function.
<span style="font-family:Microsoft YaHei;font-size:18px;">/** Interface that defines how to clone an Ref */class CC_DLL Clonable{public: /** returns a copy of the Ref */ virtual Clonable* clone() const = 0; /** * @js NA * @lua NA */ virtual ~Clonable() {}; /** returns a copy of the Ref. * @deprecated Use clone() instead */ CC_DEPRECATED_ATTRIBUTE Ref* copy() const { // use "clone" instead CC_ASSERT(false); return nullptr; }};</span>
[Callback Function Definition]
Defines the callback functions of the action, menu, and Scheduler:
<span style="font-family:Microsoft YaHei;font-size:18px;">typedef void (Ref::*SEL_CallFunc)();typedef void (Ref::*SEL_CallFuncN)(Node*);typedef void (Ref::*SEL_CallFuncND)(Node*, void*);typedef void (Ref::*SEL_CallFuncO)(Ref*);typedef void (Ref::*SEL_MenuHandler)(Ref*);typedef void (Ref::*SEL_SCHEDULE)(float);#define callfunc_selector(_SELECTOR) static_cast<cocos2d::SEL_CallFunc>(&_SELECTOR)#define callfuncN_selector(_SELECTOR) static_cast<cocos2d::SEL_CallFuncN>(&_SELECTOR)#define callfuncND_selector(_SELECTOR) static_cast<cocos2d::SEL_CallFuncND>(&_SELECTOR)#define callfuncO_selector(_SELECTOR) static_cast<cocos2d::SEL_CallFuncO>(&_SELECTOR)#define menu_selector(_SELECTOR) static_cast<cocos2d::SEL_MenuHandler>(&_SELECTOR)#define schedule_selector(_SELECTOR) static_cast<cocos2d::SEL_SCHEDULE>(&_SELECTOR)</span>
The preceding callback function is defined in two steps: Type Definition and macro definition. Taking sel_callfunco as an example, we first define a member function pointer sel_callfunco through the typedef type. sel_callfunco is a member of the ref class and receive the pointer parameters of the ref type:
Typedef void (Ref: * sel_callfunco) (ref *);
The second step is to define a macro that converts a specified function to a function pointer of the sel_callfunco type to simplify user operations:
# Define callfunco_selector (_ selector) static_cast <cocos2d: sel_callfunco> (& _ selector)
The preceding macro callfunco_selector is used to forcibly convert the Function Represented by _ selector to the definition of function pointer of sel_callfunco type through static_cast.
[Memory leakage detection]
In the ref implementation process described above, we intentionally ignored some secondary code, including memory leakage detection, which is macro:
<span style="font-family:Microsoft YaHei;font-size:18px;">#define CC_USE_MEM_LEAK_DETECTION 0</span>
Switch. The memory leak detection code mainly includes ref-type static member functions:
<span style="font-family:Microsoft YaHei;font-size:18px;">class CC_DLL Ref{ // Memory leak diagnostic data (only included when CC_USE_MEM_LEAK_DETECTION is defined and its value isn't zero)#if CC_USE_MEM_LEAK_DETECTIONpublic: static void printLeaks();#endif};</span>
Defined in ccref. static functions in the CPP file (the difference between a static function and a common function is that it is visible only in the file where it is declared, but not in other files. At the same time, functions with the same name can be defined in other files without conflict)
#if CC_USE_MEM_LEAK_DETECTIONstatic void trackRef(Ref* ref);static void untrackRef(Ref* ref);#endif
The trackref function is called when the ref class object is created, and the untrackref function is called when the ref class object is destroyed. The ref object instance is saved in the static linked list _ refallocationlist. The implementation code is as follows:
#if CC_USE_MEM_LEAK_DETECTIONstatic std::list<Ref*> __refAllocationList;void Ref::printLeaks(){ // Dump Ref object memory leaks if (__refAllocationList.empty()) { log("[memory] All Ref objects successfully cleaned up (no leaks detected).\n"); } else { log("[memory] WARNING: %d Ref objects still active in memory.\n", (int)__refAllocationList.size()); for (const auto& ref : __refAllocationList) { CC_ASSERT(ref); const char* type = typeid(*ref).name(); log("[memory] LEAK: Ref object '%s' still active with reference count %d.\n", (type ? type : ""), ref->getReferenceCount()); } }}static void trackRef(Ref* ref){ CCASSERT(ref, "Invalid parameter, ref should not be null!"); // Create memory allocation record. __refAllocationList.push_back(ref);}static void untrackRef(Ref* ref){ auto iter = std::find(__refAllocationList.begin(), __refAllocationList.end(), ref); if (iter == __refAllocationList.end()) { log("[memory] CORRUPTION: Attempting to free (%s) with invalid ref tracking record.\n", typeid(*ref).name()); return; } __refAllocationList.erase(iter);}#endif // #if CC_USE_MEM_LEAK_DETECTION
[What I was thinking when I was studying the source code of Cocos-2dx]-ref class, the origins of everything