今天第一天开始写博客,我想我也会一直的坚持坚持下去,和所有人进行分享和交流,这也是源于对技术的热爱和对游戏的炽热,我想我的这一生都将会奉献给游戏,他是我一辈子的事业更是我这辈子的朋友,好了废话就不多说了,第一篇博客我们就来聊聊cocos2dx的内存管理。 我们首先来看下下面的代码片段 class Ref { public: void retain(); void release(); Ref* autorelease(); protected: unsigned int _referenceCount; }; 而看cocos2dx里面所有的显示对象的基类Node也都公有的继承了Ref(在cocos2dx3.0以下是CCObiect),于是所有的显示对象也都有具有这三个方法,而在看看这三个方法的实现 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) { #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 autoreleasepool."); } delete this; } } Ref* Ref::autorelease() { PoolManager::getInstance()->getCurrentPool()->addObject(this); return this; }
了解object-c的人通过上面的代码应该都能够明白cocos2dx-c的内存管理其实是和object-c的一模一样的,都是采用引用计数的方法来管理自己的内存的,当创建一个对象的时候他的引用计数器_referenceCount会加一,不要问我为什么,因为_referenceCount在构造函数被初始化了1,在看下面创建一个显示对象的代码
Layer *Layer::create()
{
Layer *ret = new (std::nothrow) Layer();
if (ret && ret->init())
{
ret->autorelease();
return ret;
}
else
{
CC_SAFE_DELETE(ret);
return nullptr;
}
}
这是创建一个层的代码new Layer 这个对象的内存快,init初始化这个类里面的逻辑函数或者变量,然后autorelease然后返回,为什么要autorelease呢,看看autorelease这个函数的实现
Ref* Ref::autorelease()
{
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return this;
}
原来是把这个对象也就是这个内存快添加到一个全局的PoolManager里面,而为什么要添加到PoolManager这里面呢,看看PoolManager就一目了然
AutoreleasePool* PoolManager::getCurrentPool() const
{
return _releasePoolStack.back();
}
是获取得到一个 PoolManager 里面AutoreleasePool对象,并且把显示对象添加到AutoreleasePool对象里面的集合对象中(此处我也不太明白作者的用意,单利对象本来就只有一个实例,为毛_releasePoolStack还是一个集合,每次都要拿集合最后的一个AutoreleasePool,而框架由始至终都只往_releasePoolStack集合中添加了一AutoreleasePool对象);这样只要经过autorelease的函数就会添加到PoolManager里面来,而为什么要这样做呢,当然肯定是为了更好的管理内存,而在什么地方管理呢,我们知道cocos2d他只有一个主线程,在那呢,我们找找
void DisplayLinkDirector::mainLoop()
{
if (_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop = false;
purgeDirector();
}
else if (! _invalid)
{
drawScene();
// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
我们看到了PoolManager::getInstance()->getCurrentPool()->clear();这一句,其他的我们以后章节在来讲他们的愿意,本章节我们只看框架的内存管理模块,我们继续看PoolManager里面的clear方法
void AutoreleasePool::clear()
{
for (const auto &obj : _managedObjectArray)
{
obj->release();
}
_managedObjectArray.clear();
}
他会调用所有添加到AutoreleasePool对象的release方法,看来release非常重要,我们继续研究release方法
if (_referenceCount == 0)
{
auto poolManager = PoolManager::getInstance();
if (!poolManager->getCurrentPool()->isClearing() && poolManager->isObjectInPools(this)) { delete this; } }
这下应该能够一目了然了,当显示对象的引用计数器为0的时候他就会自动的删除自己,所以这样就能够很巧妙的管理创建或者说是new出来的对象了,而当什么时候引用计数器会加1什么时候会减1呢我们继续深入的研究
void Node::insertChild(Node* child,int z)
{
_transformUpdated = true;
_reorderChildDirty = true;
_children.pushBack(child);
child->_localZOrder = z;
}
当一个对象往另外一个对象上添加时,都会调用addChild方法,而addChild必调用insertChild方法,而insertChild方法其中的 _children.pushBack(child)这一句
void pushBack(T object)
{
CCASSERT(object != nullptr,"The object should not be nullptr");
_data.push_back( object );
object->retain();
}
他会调用retain方法,retain会将引用计数器加1现在_referenceCount = 2,而当移除显示对象时必须会调用removeChild,则他同时也会调用release()函数,此时_referenceCount会–_referenceCount会变成1,当程序执行下一帧的时候,他继续会–_referenceCount;然后当_referenceCount = 0的时候就会删除此对象 这样就构成了一个先是对象从产生到销毁的整个过程,所以当我们使用的时候就一定要注意了,手动retain的使用完毕后,必须要手动调用release(),否则他永远不会删除这段内存,从而造成内存泄漏。