首先让我们从C/C++的内存管理开始说起。
C语言的创建释放内存使用的是 malloc 和free
c++ 中是 new 和delete
在cocos2dx里面,可以使用c和c++的,也可以使用封装过的 retain和release 间接的去管理内存。
void Ref::retain()
{
CCASSERT(_referenceCount > 0, "reference count should be greater than 0");
++_referenceCount; //引用计数++
}
void Ref::release()
{
CCASSERT(_referenceCount > 0, "reference count should be greater than 0");
--_referenceCount; //引用计数--
//中间去掉了无关代码
if (_referenceCount == 0) //引用计数为0的时候delete this
{
#if CC_REF_LEAK_DETECTION //内存泄露
untrackRef(this);
#endif
delete this;
}
}
下面我们去Ref类中看看_referenceCount刚创建的时候默认值是多少
Ref::Ref()
: _referenceCount(1) // 一个ref对象一开始创建的时候引用计数是1
{
#if CC_ENABLE_SCRIPT_BINDING
static unsigned int uObjectCount = 0;
_luaID = 0;
_ID = ++uObjectCount;
_scriptObject = nullptr;
#endif
#if CC_REF_LEAK_DETECTION
trackRef(this);
#endif
}
#define CREATE_FUNC(__TYPE__) \
static __TYPE__* create() \
{ \
__TYPE__ *pRet = new(std::nothrow) __TYPE__(); \
if (pRet && pRet->init()) \
{ \
pRet->autorelease(); \ //创建成功后第一件事就把对象放入自动释放池。以达到自动管理内存,自动释放指针对应的内存的效果。
return pRet; \
} \
else \
{ \
delete pRet; \
pRet = NULL; \
return NULL; \
} \
}
Ref* Ref::autorelease()
{
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return this;
}
一眼就看出,加入自动释放池。
void AutoreleasePool::addObject(Ref* object)
{
_managedObjectArray.push_back(object);
}
之前说了要想释放一个Ref对象,调用Ref::release就行了。
AutoreleasePool要自动释放肯定会调用这个函数,在这个类中搜一下release(),
果然在AutoreleasePool::clear中找到了。
void AutoreleasePool::clear()
{
std::vector releasings;
releasings.swap(_managedObjectArray);//交换 _managedObjectArray 和 releasings
//交换之后_managedObjectArray会置为空集合,也就是说,每个ref类都只会自动释放一次。也就是初始化的引用计数--变成0。
//也就解释了为什么刚创建的ref对象,如果不被addchild就会被释放的情况。 下次使用的时候会内存出错。
for (const auto &obj : releasings)
{
obj->release();//一个个释放
}
}
找到了,是Director导演类的的主循环中。
void DisplayLinkDirector::mainLoop()
{
if (_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop = false;
purgeDirector();
}
else if (_restartDirectorInNextLoop)
{
_restartDirectorInNextLoop = false;
restartDirector();
}
else if (! _invalid)
{
drawScene();
// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();//这里是主循环,每一帧都会执行,也解释了为什么叫自动释放。。。
}
}
具体的代码,可以自己去断点查看或者自己猜猜。
retain大致位置是在 insertChild ->_children.pushBack
release大致是在 detachChild -> _children.erase()中