先考虑个问题。
为什么要引进内存管理?
c++不像java,c#有自己的垃圾回收机制。所以很容易出现用完的内存空间没有被回收,即内存泄露。所以要引进内存管理。
cocos2dx用到的内存管理技术是引用技术。即当对象增加一次引用时,计数器加一;减少一时,计数器减一。分别对应引用计数类Ref的retain()和rrelease();
class CC_DLL Ref
{
public:
/**
* 增加一次引用计数
*/
void retain();
/**
* 释放一次计数,具体里面怎么释放的,下面我们跟定义的代码做分析
*/
void release();
/**
* 自动释放
*/
Ref* autorelease();
/**
* 得到当前对象的引用计数的值,也是是被引用了多少次
*/
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:
/// 用来记录引用次数的变量值
unsigned int _referenceCount;
friend class AutoreleasePool;
#if CC_ENABLE_SCRIPT_BINDING
public:
/// 对象的ID
unsigned int _ID;
/// 这个变量这里猜测是lua脚本中引用的ID
int _luaID;
#endif
};
凡是继承了Ref的类都是自动释放类,这些类每次创建完之后,引用都为1。看到Ref有个autorelease()和一个友元类AutoreleasePool,这是内存管理用到的一个很重要的类。
那他们是干嘛的呢?
我们知道类创建完后,如果没被用到或不用了,内存是需要被回收的,否则就会出现内存泄露的情况。cocos2dx每一帧结束都会进行内存回收。
怎么回收呢?
先把调用了autorelease的对象加入到AutoreleasePool这个回收池里面,每一帧结束后将AutoreleasePool里的所有对象进行一次release(),并清空。
Ref* Ref::autorelease()
{
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return this;
}
void DisplayLinkDirector::mainLoop()
{
if (_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop = false;
purgeDirector();
}
else if (! _invalid)
{
drawScene();
// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
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
}
void Ref::release()
{
CCASSERT(_referenceCount > 0, "reference count should 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
delete this;
}
}
对象被创建的时候引用为1,如果在一帧结束前,对象被其他类用到了,然后该对象调用了retain(),引用为2,那么AutoreleasePool释放的时候调用release()后,引用为1,则不会回收;
如果在一帧结束前,一直未调用retain(),那么就将被回收。