標籤:des style color io os 使用 ar java for
【名詞解釋】
引用計數:引用計數是現代記憶體管理中經常使用到的一個概念,它的基本思想是通過計數方式實現多個不同對象同時引用一個共用對象,具體地講,當建立一個對象的執行個體並在堆上分配記憶體時,對象的引用計數為1,在其他對象中需要持有這個共用對象時,需要把共用對象的引用計數加1,當其他對象不再持有該共用對象時,共用對象的引用計數減1,當共用對象的引用計數變成0時,對象的記憶體會被立即釋放。(部分截取自維基百科)。
比較著名的使用引用計數的有COM和Objective-C,在COM的IUnknow介面中定義了三個函數:QueryInterface,AddRef和Release,它們分別用於擷取介面對象、給介面對象增加計數,給介面對象減少計數,當內部計數變為0時,自動銷毀介面對象。在Objective-C中,則定義了retain,release和autorelease函數,分別用於增加計數、減少計數以及將一個對象交給自動釋放池對象AutoreleasePool進行管理,由AutoreleasePool對象負責調用release函數。
【Ref類的實現】
由於Cocos2d-x是在Cocos2d-iPhone的基礎上發展而來,所以沿用了很多Objective-C的思想,Ref類的實現就是如此。
Ref類實現了引用計數的功能,它是引擎代碼中絕大多數其他類的父類,定義在CCRef.h中,實現在CCRef.cpp中。其實在CCRef.h檔案中不止定義了Ref類,還定義了Clonable類、一系列的宏定義和類型定義,不過我們暫且將精力放在Ref類的解讀上。Ref類使用私人成員變數_referenceCount儲存計數值,並通過retain,release和autorelease函數實現增減計數值。
<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將建構函式聲明為保護類型,防止直接產生Ref對象,在建構函式的成員初始化列表中將引用計數值_referenceCount初始化為1。retain函數將_referenceCount加1,release函數則減1,autorelease函數則將對象託管給AutoreleasePool對象進行管理,具體實現代碼如下:
<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>【Clonable類的定義】
Clonable類定義了複製Ref類對象的規約,類似於Java語言中介面java.lang.Clonable的作用,3.2版本建議使用clone函數,copy函數已屬於廢棄函數。
<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>【回呼函數的定義】
定義了動作、菜單和調度器的回呼函數:
<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>
上面回呼函數的定義分為兩步:類型定義和宏定義。我們以SEL_CallFuncO為例進行說明,首先通過typedef類型定義了一個成員函數指標SEL_CallFuncO,SEL_CallFuncO是Ref類的成員,同時接收Ref類型的指標形參:
typedef void (Ref::*SEL_CallFuncO)(Ref*);
第二步是定義將指定函數轉換為SEL_CallFuncO類型函數指標的宏,簡化使用者操作:
#define callfuncO_selector(_SELECTOR) static_cast<cocos2d::SEL_CallFuncO>(&_SELECTOR)
上面這個宏CallFuncO_selector的作用就是將_SELECTOR表示的函數,通過static_cast強制轉換為SEL_CallFuncO類型的函數指標的定義。
【記憶體流失的檢測】
在上面講解Ref實現過程中,我們故意忽略了一些次要的代碼,其中就包括記憶體流失檢測,這部分代碼是以宏:
<span style="font-family:Microsoft YaHei;font-size:18px;">#define CC_USE_MEM_LEAK_DETECTION 0</span>
作為開關的。記憶體流失檢測代碼主要包括Ref類靜態成員函數:
<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>
定義在CCRef.cpp檔案內的靜態函數(靜態函數與普通函數不同之處在於,它只在聲明它的檔案中可見,其他檔案不可見,同時,其他檔案中可以定義相同名字的函數,不會發生衝突)
#if CC_USE_MEM_LEAK_DETECTIONstatic void trackRef(Ref* ref);static void untrackRef(Ref* ref);#endif
trackRef函數在Ref類對象建立的時候調用,untrackRef在Ref類對象銷毀的時候調用,Ref對象執行個體儲存在靜態鏈表__refAllocationList中,實現代碼如下所示:
#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
[當我在研究Cocos-2dx的源碼時,我在想什麼]-Ref類,一切的起源