cocos2d-x版本:3.17.2
运行环境:Visual Studio 2017
解决方案配置:Debug Win32
class CC_DLL Ref
{
public:
// 增加一次引用计数
void retain();
// 减少一次引用计数
void release();
// 自动释放
Ref* autorelease();
unsigned int getReferenceCount() const;
protected:
Ref();
public:
virtual ~Ref();
protected:
// 用来记录引用次数的变量值
unsigned int _referenceCount;
// 自动释放对象池
friend class AutoreleasePool;
};
Ref这个类使用_referenceCount来记录引用次数的变量值。Ref的构造函数声明为 protected 的访问权限,那么这个Ref类是不可以被直接实例化的 只能有子类来实例化这个对象。
// 构造函数
Ref::Ref()
: _referenceCount(1) // when the Ref is created, the reference count of it is 1
#if CC_ENABLE_SCRIPT_BINDING
, _luaID (0)
, _scriptObject(nullptr)
, _rooted(false)
#endif
{
#if CC_ENABLE_SCRIPT_BINDING
static unsigned int uObjectCount = 0;
_ID = ++uObjectCount;
#endif
#if CC_REF_LEAK_DETECTION
trackRef(this);
#endif
}
当Ref被创建的时候,引用计数自动设为1。CC_ENABLE_SCRIPT_BINDING宏定义表示支持脚本绑定,这里定义了一个静态变量uObjectCount用来记录创建的对象数量,当Ref的子类创建对象的时候,基类的Ref的构造函数里 uobjectCount就会自增1,这也使所有对象都有唯一的_ID值不会重复。
// 析构函数
Ref::~Ref()
{
#if CC_ENABLE_SCRIPT_BINDING
ScriptEngineProtocol* pEngine = ScriptEngineManager::getInstance()->getScriptEngine();
if (pEngine != nullptr && _luaID)
{
// if the object is referenced by Lua engine, remove it
pEngine->removeScriptObjectByObject(this);
}
#if !CC_ENABLE_GC_FOR_NATIVE_OBJECTS
else
{
if (pEngine != nullptr && pEngine->getScriptType() == kScriptTypeJavascript)
{
pEngine->removeScriptObjectByObject(this);
}
}
#endif // !CC_ENABLE_GC_FOR_NATIVE_OBJECTS
#endif // CC_ENABLE_SCRIPT_BINDING
#if CC_REF_LEAK_DETECTION
if (_referenceCount != 0)
untrackRef(this);
#endif
}
对象销毁的时候,会去通知脚本管理器ScriptEngineManager消除对象。
// Retain函数 增加一次引用计数
void Ref::retain()
{
CCASSERT(_referenceCount > 0, "reference count should be greater than 0");
++_referenceCount;
}
// Release函数,减少一次引用计数。当引用计数为0的时候,删除该对象
void Ref::release()
{
CCASSERT(_referenceCount > 0, "reference count should be greater than 0");
--_referenceCount;
if (_referenceCount == 0)
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
auto poolManager = PoolManager::getInstance();
if (!poolManager->getCurrentPool()->isClearing() && poolManager->isObjectInPools(this))
{
CCASSERT(false, "The reference shouldn't be 0 because it is still in autorelease pool.");
}
#endif
#if CC_ENABLE_SCRIPT_BINDING
ScriptEngineProtocol* pEngine = ScriptEngineManager::getInstance()->getScriptEngine();
if (pEngine != nullptr && pEngine->getScriptType() == kScriptTypeJavascript)
{
pEngine->removeObjectProxy(this);
}
#endif // CC_ENABLE_SCRIPT_BINDING
#if CC_REF_LEAK_DETECTION
untrackRef(this);
#endif
delete this;
}
}
分析代码,Release函数就干了两件事
Ref* Ref::autorelease()
{
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return this;
}
autorelease函数则没有减少引用次数,而是直接调用PoolManager的内存管理池来进行处理。
现查看PoolManager类
// 管理所有的AutoreleasePool的管理器
class CC_DLL PoolManager
{
public:
CC_DEPRECATED_ATTRIBUTE static PoolManager* sharedPoolManager() { return getInstance(); }
static PoolManager* getInstance();
CC_DEPRECATED_ATTRIBUTE static void purgePoolManager() { destroyInstance(); }
static void destroyInstance();
// 获取当前AutoreleasePool
AutoreleasePool *getCurrentPool() const;
bool isObjectInPools(Ref* obj) const;
friend class AutoreleasePool;
private:
PoolManager();
~PoolManager();
void push(AutoreleasePool *pool);
void pop();
static PoolManager* s_singleInstance;
std::vector<AutoreleasePool*> _releasePoolStack;
};
// 用于管理自动释放对象的池
class CC_DLL AutoreleasePool
{
public:
AutoreleasePool();
AutoreleasePool(const std::string &name);
~AutoreleasePool();
void addObject(Ref *object);
void clear();
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
// 判断自动释放池是否正在执行`clear`操作。
bool isClearing() const { return _isClearing; };
#endif
// 检查自动释放池是否包含指定的对象。
bool contains(Ref* object) const;
void dump();
private:
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
};
二者的关系:PoolManager用栈的形式管理AutoreleasePool,从其构造函数与析构函数可以看出,每新创建一个AutoreleasePool就push进栈,而销毁一个AutoreleasePool就pop出栈。每一次通过PoolManager::tCurrentPool()函数获取的都是栈顶当前的AutoreleasePool.
其中主要看AutoreleasePool的clear函数
void AutoreleasePool::clear()
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
_isClearing = true;
#endif
std::vector<Ref*> releasings;
releasings.swap(_managedObjectArray);
for (const auto &obj : releasings)
{
obj->release();
}
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
_isClearing = false;
#endif
}
该函数遍历了对象列表里面的每一个Ref对象,调用它的release方法,减少对象的引用次数,然后检查那些引用次数为0的对象,将其销毁。而何时调用这个clear函数呢,实际上在我们Director的mainLoop中每一次循环结束都会去释放一次自动管理的对象。
cocos很多类型继承了 Ref,这是因为为了放到内存管理器里面统一管理,并且可以在类型创建的时候就已经使用到了autorelease来做内存释放。新创建的对象直接加入到自动的内存管理里面,来实现自动释放,这样避免了我们到处去想着delete,更加安全方便。