内存管理是C/C++开发人员不可避免的问题,也是C/C++最有争议的问题,C/C++高手从中获得了更好的性能,更大的自由,作为很多C/C++的初学者来说 《C语言从入门到放弃》 更能表达学习的心情。
C/C++的灵魂拷问
既然讲内存管理,我门首先介绍下C++的内存分配方式
一、内存分配方式
在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。
栈,在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。
全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改。
那我们在cocos中也需要每次都面对这样的 灵魂拷问 么,当然不是啦,在cocos2dx中为我们封装了autorelease为我们自动释放。
二、Cocos2dx 内存管理
我们在前面的教程中很早就见到了如下的宏定义
#define CREATE_FUNC(__TYPE__) \
static __TYPE__* create() \
{ \
__TYPE__ *pRet = new __TYPE__(); \
if (pRet && pRet->init()) \
{ \
pRet->autorelease(); \
return pRet; \
} \
else \
{ \
delete pRet; \
pRet = NULL; \
return NULL; \
} \
}
当我们看到如下这句时,我们已经接触到了Cocos2dx的内存管理
pRet->autorelease();
自动释放喽,是不是很贴心,当然在贴心的同时还是要注意一下的,下面具体介绍下Cocos2d-x的内存管理。
Cocos2dx内存管理类有:
Ref类;
AutoreleasePool类;
PoolManager类。
Ref类几乎是Cocos2d-x中所有类的父类,它是Cocos2d-x中内存管理的最重要的一环;上面说的autorelease函数就Ref类的成员函数,Cocos2d-x中所有继承自Ref的类,都可以使用Cocos2d-x的内存管理。
AutoreleasePool类用来管理自动释放对象。
PoolManager用来管理所有的AutoreleasePool,这个类是使用单例模式实现的。
new和autorelease需要匹配使用,retain和release也需 要匹配使用,否则就会出现断言错误,或者内存泄露;在非Debug模式下,就可能直接闪退了。这就是为什么我们在使用create函数的时候,new成功 以后,就顺便调用了autorelease,将该对象放入到自动释放池中;而当我们再次想获取该对象并使用该对象的时候,需要使用retain再次获得该 对象的所有权,当然了,在使用完成以后,你应该记得调用release去手动完成释放工作,这是你的任务
这里retain和release对应,release一个已经被autorelease过的对象(例如通过create函数构造的对象)必须先retain
对于AutoreleasePool类来说,它的实现很简单,就是将简单的将对象保存在一个std::vector中,在释放这个AutoreleasePool的时候,对保存在std::vector中的对象依次调用对应的release函数,从而完成对象的自动释放。
我们把AutoreleasePool对象又放到了PoolManager里了;原 来,PoolManager类就是用来管理所有的AutoreleasePool的类,也是使用的单例模式来实现的。该PoolManger有一个存放 AutoreleasePool对象指针的stack,该stack是由std::vector实现的。
在Cocos2d-x中,很多地方已 经进行了autorelease,或者retain了,我们就不必再次进行这些操作,比如create,再比如在调用addChild方法添加子节点时, 自动调用了retain。对应的通过removeChild,移除子节点时,自动调用了release。
void DisplayLinkDirector::mainLoop()
{
if (_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop = false;
purgeDirector();
}
else if (! _invalid)
{
drawScene();
// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
看到这是不是感觉方便多啦,我们可以把更多的精力投入到更好的处理游戏逻辑上去啦