從cocos的CCNode.h的可以看出Node是繼承與Ref,首先我們先看CCRef.h文件,如下:
//CCRef.h
#include "base/CCPlatformMacros.h"
#include "base/ccConfig.h"#define CC_USE_MEM_LEAK_DETECTION 0
NS_CC_BEGIN
/**
* @addtogroup base_nodes
* @{
*/
class Ref;
/** 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;
}
};
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;
#if CC_ENABLE_SCRIPT_BINDING
public:
/// object id, ScriptSupport need public _ID
unsigned int _ID;
/// Lua reference id
int _luaID;
#endif
// 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_DETECTION
public:
static void printLeaks();
#endif
};
class Node;
//定義的回調方式
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)
// end of base_nodes group
/// @}
NS_CC_END
#endif // __BASE_CCREF_H__
從文件裏面看到了release,retain,autorelease這些與cocos內存管理有關的函數,現在在分析一下實現的文件CCRef.cpp
//CCRef.cpp
#include "base/CCRef.h"
#include "base/CCAutoreleasePool.h"
#include "base/ccMacros.h"
#include "base/CCScriptSupport.h"
#if CC_USE_MEM_LEAK_DETECTION
#include <algorithm> // std::find
#endif
NS_CC_BEGIN
//內存追蹤宏
#if CC_USE_MEM_LEAK_DETECTION
static void trackRef(Ref* ref);
static void untrackRef(Ref* ref);
#endif
//構造函數,引用計數值初始化1
Ref::Ref()
: _referenceCount(1) // when the Ref is created, the reference count of it is 1
{
#if CC_ENABLE_SCRIPT_BINDING
static unsigned int uObjectCount = 0;
_luaID = 0;
_ID = ++uObjectCount;
#endif
#if CC_USE_MEM_LEAK_DETECTION
trackRef(this);
#endif
}
//析構函數,如果使用了腳本,清除腳本對象
Ref::~Ref()
{
#if CC_ENABLE_SCRIPT_BINDING
// if the object is referenced by Lua engine, remove it
if (_luaID)
{
ScriptEngineManager::getInstance()->getScriptEngine()->removeScriptObjectByObject(this);
}
else
{
ScriptEngineProtocol* pEngine = ScriptEngineManager::getInstance()->getScriptEngine();
if (pEngine != NULL && pEngine->getScriptType() == kScriptTypeJavascript)
{
pEngine->removeScriptObjectByObject(this);
}
}
#endif
//使用了內存泄露跟蹤,如果引用計數不爲0,清除
#if CC_USE_MEM_LEAK_DETECTION
if (_referenceCount != 0)
untrackRef(this);
#endif
}
//調用了retain後,會使引用計數的值加1
void Ref::retain()
{
CCASSERT(_referenceCount > 0, "reference count should greater than 0");
++_referenceCount;
}
//使用了release後,會把引用計數的值減1,當引用計數的值等於0時會刪除該對象,所以當你如果使用了retain函數後,引用計數的值其實應該爲2,然後在次調用release會進行引用計數值減1,但引用計數的值爲1,所以該對象是不會被刪除的。可以修改引用計數的值初始化爲1,當調用release以後值就是爲0,會被刪除掉。這裏發現一個問題,cocos只進行了delete this,並沒有把this=nullptr,不知這樣會不會出現野指針,以後驗證一下,這裏只是猜測一下。。
void Ref::release()
{
CCASSERT(_referenceCount > 0, "reference count should greater than 0");
--_referenceCount;
if (_referenceCount == 0)
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
...........................................
#endif
#if CC_USE_MEM_LEAK_DETECTION
untrackRef(this);
#endif
delete this;
}
}
//當調用autorelease以後,該對象的內存管理就交給了cocos的內存池管理器,內存管理器具體內容,稍後在分析
Ref* Ref::autorelease()
{
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return this;
}
//顯然這個函數是獲得引用計數的值
unsigned int Ref::getReferenceCount() const
{
return _referenceCount;
}
//這是cocos內存泄露的檢查,沒使用過,具體不清楚,以後體驗一下
#if CC_USE_MEM_LEAK_DETECTION
static 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
NS_CC_END
接下了是cocos內存池管理器的實現
//首先是頭文件
//CCAutoReleasePool.h
#ifndef __AUTORELEASEPOOL_H__
#define __AUTORELEASEPOOL_H__
#include <stack>
#include <vector>
#include <string>
#include "base/CCRef.h"
NS_CC_BEGIN
/**
* @addtogroup base_nodes
* @{
*/
class CC_DLL AutoreleasePool
{
public:
/**
* @warn Don't create an auto release pool in heap, create it in stack.
* @js NA
* @lua NA
*/
AutoreleasePool();
/**
* Create an autorelease pool with specific name. This name is useful for debugging.
*/
AutoreleasePool(const std::string &name);
/**
* @js NA
* @lua NA
*/
~AutoreleasePool();
/**
* Add a given object to this pool.
*
* The same object may be added several times to the same pool; When the
* pool is destructed, the object's Ref::release() method will be called
* for each time it was added.
*
* @param object The object to add to the pool.
* @js NA
* @lua NA
*/
void addObject(Ref *object);
/**
* Clear the autorelease pool.
*
* Ref::release() will be called for each time the managed object is
* added to the pool.
* @js NA
* @lua NA
*/
void clear();
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
/**
* Whether the pool is doing `clear` operation.
*/
bool isClearing() const { return _isClearing; };
#endif
/**
* Checks whether the pool contains the specified object.
*/
bool contains(Ref* object) const;
/**
* Dump the objects that are put into autorelease pool. It is used for debugging.
*
* The result will look like:
* Object pointer address object id reference count
*
*/
void dump();
private:
/**
* The underlying array of object managed by the pool.
*
* Although Array retains the object once when an object is added, proper
* Ref::release() is called outside the array to make sure that the pool
* does not affect the managed object's reference count. So an object can
* be destructed properly by calling Ref::release() even if the object
* is in the pool.
*/
std::vector<Ref*> _managedObjectArray;
std::string _name;
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
/**
* The flag for checking whether the pool is doing `clear` operation.
*/
bool _isClearing;
#endif
};
class CC_DLL PoolManager
{
public:
/**
* @js NA
* @lua NA
*/
CC_DEPRECATED_ATTRIBUTE static PoolManager* sharedPoolManager() { return getInstance(); }
static PoolManager* getInstance();
/**
* @js NA
* @lua NA
*/
CC_DEPRECATED_ATTRIBUTE static void purgePoolManager() { destroyInstance(); }
static void destroyInstance();
/**
* Get current auto release pool, there is at least one auto release pool that created by engine.
* You can create your own auto release pool at demand, which will be put into auto releae pool stack.
*/
AutoreleasePool *getCurrentPool() const;
bool isObjectInPools(Ref* obj) const;
/**
* @js NA
* @lua NA
*/
friend class AutoreleasePool;
private:
PoolManager();
~PoolManager();
void push(AutoreleasePool *pool);
void pop();
static PoolManager* s_singleInstance;
std::deque<AutoreleasePool*> _releasePoolStack;
AutoreleasePool *_curReleasePool;
};
// end of base_nodes group
/// @}
NS_CC_END
#endif //__AUTORELEASEPOOL_H__
//CCAutoReleasePool.cpp
#include "base/CCAutoreleasePool.h"
#include "base/ccMacros.h"
NS_CC_BEGIN
//AutoreleasePool的構造函數,在此處初始化一個vector的變量,空間爲150,並把當前autoreleasePool的對象放入PoolManage維持的一個vector當中,AutoReleasePool的有兩個構造函數,cocos會默認調用帶參數的構造函數,後面會分析爲什麼會調用帶參數的構造函數
AutoreleasePool::AutoreleasePool()
: _name("")
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
, _isClearing(false)
#endif
{
_managedObjectArray.reserve(150);
PoolManager::getInstance()->push(this);
}
AutoreleasePool::AutoreleasePool(const std::string &name)
: _name(name)
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
, _isClearing(false)
#endif
{
_managedObjectArray.reserve(150);
PoolManager::getInstance()->push(this);
}
//autoRelesae的析構函數,從PoolManager的Vector中彈出autorelease的對象
AutoreleasePool::~AutoreleasePool()
{
CCLOGINFO("deallocing AutoreleasePool: %p", this);
clear();
PoolManager::getInstance()->pop();
}
//把繼承Ref的子節點放入AutoreleasePool的vector中
void AutoreleasePool::addObject(Ref* object)
{
_managedObjectArray.push_back(object);
}
//刪除vector裏面的對象
void AutoreleasePool::clear()
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
_isClearing = true;
#endif
for (const auto &obj : _managedObjectArray)
{
obj->release();
}
_managedObjectArray.clear();
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
_isClearing = false;
#endif
}
//判斷對象是否在autoreleasePool當中
bool AutoreleasePool::contains(Ref* object) const
{
for (const auto& obj : _managedObjectArray)
{
if (obj == object)
return true;
}
return false;
}
void AutoreleasePool::dump()
{
CCLOG("autorelease pool: %s, number of managed object %d\n", _name.c_str(), static_cast<int>(_managedObjectArray.size()));
CCLOG("%20s%20s%20s", "Object pointer", "Object id", "reference count");
for (const auto &obj : _managedObjectArray)
{
CC_UNUSED_PARAM(obj);
CCLOG("%20p%20u\n", obj, obj->getReferenceCount());
}
}
//--------------------------------------------------------------------
//
// PoolManager
//
//--------------------------------------------------------------------
//單例模式實現了內存管理池
PoolManager* PoolManager::s_singleInstance = nullptr;
PoolManager* PoolManager::getInstance()
{
if (s_singleInstance == nullptr)
{
s_singleInstance = new PoolManager();
// Add the first auto release pool
//實例化了autoreleasePool對象,調用了但參數的構造函數,並放入poolmanager的vector當中,並賦值給poolmanager的curReleasePool
s_singleInstance->_curReleasePool = new AutoreleasePool("cocos2d autorelease pool");
s_singleInstance->_releasePoolStack.push_back(s_singleInstance->_curReleasePool);
}
return s_singleInstance;
}
//PoolManager的清除
void PoolManager::destroyInstance()
{
delete s_singleInstance;
s_singleInstance = nullptr;
}
PoolManager::PoolManager()
{
}
//析構函數,清除vector
PoolManager::~PoolManager()
{
CCLOGINFO("deallocing PoolManager: %p", this);
while (!_releasePoolStack.empty())
{
AutoreleasePool* pool = _releasePoolStack.back();
_releasePoolStack.pop_back();
delete pool;
}
}
//返回當前的autoreleasePool的對象
AutoreleasePool* PoolManager::getCurrentPool() const
{
return _curReleasePool;
}
//判斷是否在cocos的自動內存管理
bool PoolManager::isObjectInPools(Ref* obj) const
{
for (const auto& pool : _releasePoolStack)
{
if (pool->contains(obj))
return true;
}
return false;
}
//把autoReleasePOOL放入PoolManager當中
void PoolManager::push(AutoreleasePool *pool)
{
_releasePoolStack.push_back(pool);
_curReleasePool = pool;
}
//把autoreleasePOOL從PoolManager當中刪除
void PoolManager::pop()
{
// Can not pop the pool that created by engine
CC_ASSERT(_releasePoolStack.size() >= 1);
_releasePoolStack.pop_back();
// Should update _curReleasePool if a temple pool is released
if (_releasePoolStack.size() > 1)
{
_curReleasePool = _releasePoolStack.back();
}
}
NS_CC_END
從以上的分析可知,cocos的內存管理是採用Poolmanager來管理,裏面管理的是AutoReleasePool的對象,而繼承與Ref的子節點都是AutoRelasePool來管理的,因爲他們都放入了AutoReleasePool維持的一個verctor當中。即AutoReleasePool包含了繼承Ref的子節點,而PoolManager則保存了AutoReleasePool的對象。
以上是個人的理解,有不當的地方,請大神指出,不勝感激!!!!