C++内存管理:
栈上的空间:不用程序员手动管理。
堆上的空间:new申请的空间需要用delete来释放,否则产生内存泄漏。
过程型函数:函数中每条语句执行完毕后,返回到调用此函数的地方。
注册型函数:注册型函数把此函数注册到某个地方后立即返回,该函数不立即执行,等待注册到的地方来执行该函数。
内存的两种智能管理:
主要有两种实现智能管理内存的技术,一是引用计数,一是垃圾回收。
引用计数:
通过给每个对象维护一个引用计数器,记录该对象当前被引用的次数。当对象增加一次引用时,计数器加1;而对象失去一次引用时,计数器减1;当引用计数为0 时,标志着该对象的生命周期结束,自动触发对象的回收释放。引用计数解决了对象的生命周期管理问题,但堆碎片化和管理烦琐的问题仍然存在。
垃圾回收:
它通过引入一种自动的内存回收器,试图将程序员从复杂的内存管理任务中完全解放出来。它会自动跟踪每一个对象的所有引用,以便找到所有正在使用的对象,然后释放其余不再需要的对象。垃垃圾回收器通常是作为一个单独的低级别的线程运行的,在不可预知的情况下对内存堆中已经死亡的或者长时间没有使用过的对象进行清除和回收。
Cocos2dx 采用工厂方法创建对像,所生成的对像均在堆上,所以Cocos2dx 采用了引用计数的方法管理内存。
关于引用计数,在CCObject中定义,所有CCObject的派生类,都继承了引用计数方式管理内存。
CCObject头文件如下:
class CC_DLL CCObject : public CCCopying
{
protected:
// count of references
unsigned int m_uReference;
// count of autorelease
unsigned int m_uAutoReleaseCount;
public:
CCObject(void);
void release(void);
void retain(void);
CCObject* autorelease(void);
}
unsigned int m_uReference:记录了对象被引用的次数。
unsigned int m_uAutoReleaseCount:记录了对象被加入自动内存管理的次数。
在CCObject的构造函数中,m_uReference被初始化为1,m_uAutoReleaseCount被初始化为0,构造函数如下:
CCObject::CCObject(void)
: m_nLuaID(0)
, m_uReference(1) // when the object is created, the reference count of it is 1
, m_uAutoReleaseCount(0)
{
static unsigned int uObjectCount = 0;
m_uID = ++uObjectCount;
}
void retain(void);用来对引用计数m_uReference加一,代码如下:
void CCObject::retain(void)
{
CCAssert(m_uReference > 0, "reference count should greater than 0");
++m_uReference;
}
void release(void):用来对引用计数m_uReference减一,如果引用计数m_uReference值为0,则释放内存;
代码如下:
void CCObject::release(void)
{
CCAssert(m_uReference > 0, "reference count should greater than 0");
--m_uReference;
if (m_uReference == 0)
{
delete this;
}
}
CCObject* autorelease(void):函数用来把对象放入内存池中实现自动内存管理,实际上是把该对象在一帧的最后实现延迟释放。
CCObject* CCObject::autorelease(void)
{
CCPoolManager::sharedPoolManager()->addObject(this);
return this;
}
上面的CCPoolManager::sharedPoolManager()->addObject(this)用来把当前对象添加如内存管理中,CCPoolManager::sharedPoolManager()为注册型函数,不会立即执行;然后再来看看CCPoolManager类中内容;如下代码:
class CC_DLL CCPoolManager
{
public:
void removeObject(CCObject* pObject);
void addObject(CCObject* pObject);
static CCPoolManager* sharedPoolManager();
static void purgePoolManager();
friend class CCAutoreleasePool;
};
在CCPoolManager中定义了addObject方法:
void CCPoolManager::addObject(CCObject* pObject)
{
getCurReleasePool()->addObject(pObject);
}
当继续查看addObject时候会发现CCPoolManager是调用其他类的addObject方法,而这个类就是CCAutoreleasePool:
class CC_DLL CCAutoreleasePool : public CCObject
{
public:
void addObject(CCObject *pObject);
void removeObject(CCObject *pObject);
};
类CCAutoreleasePool中的addObject函数具体内容如下:
void CCAutoreleasePool::addObject(CCObject* pObject)
{
m_pManagedObjectArray->addObject(pObject);
CCAssert(pObject->m_uReference > 1, "reference count should be greater than 1");
++(pObject->m_uAutoReleaseCount);
pObject->release(); // no ref count, in this case autorelease pool added.
}
上述代码 ++(pObject->m_uAutoReleaseCount);实现了对CCObject类中自动内存管理计数m_uAutoReleaseCount值的计数。
到此为止,介绍完CCObject中关于引用计数与自动内存管理的函数。
当我们创建一个由CCNode派生类的对象时候,把对象加入渲染树中时使用addChild,
重点关注一下addChild,
此addChild最终会对当前对象retain(),代码如下:
void ccArrayAppendObject(ccArray *arr, CCObject* object)
{
CCAssert(object != NULL, "Invalid parameter!");
object->retain();
arr->arr[arr->num] = object;
arr->num++;
}
由此可知在使用addChild把游戏元素添加到渲染树中时候会为该对象自动retain()一次,增加一次引用计数。
当我们创建的CCLayer或者CCSprite的对象使用removeFromParent会直接把该对象在渲染树中移除并销毁对象。
而无论是CCLayer的创建还是CCSprite创建,都会使用create()工厂方法,该方法中对用new创建的对象使用了autorelease()方法,把对象添加到自动内存管理中;这个时候无论是CCLayer还是CCSprite如果不添加到渲染树中,刚刚创建的对象就会在下一个帧循环开始前被释放。
onEnter和onExit:
CCArray是我们需要特别留意的一点,CCArray也是使用工厂方法creat进行创建,在creat中对CCArray对象延迟释放,而CCArray又没有被添加到渲染树中,所以在一帧之后CCArray对象就不存在了;如果想在一帧之后继续使用CCArray,我们需要手动对CCArray对象执行retain()。
因为我们手动对CCArray对象执行retain了,所以我们必须自己来在CCArray对象不再使用时候释放它。此时需要用到onExit()。
class CC_DLL CCLayer : public CCNode, public CCTouchDelegate, public CCAccelerometerDelegate, public CCKeypadDelegate
{
public:
virtual void onEnter();
virtual void onExit();
}
onEnter:init初始完后进入,可能调用多次。
onExit:退出时候调用。
可以把CCArray对象的release写在onExit中。
总结:借用一个老师总结的
在一帧开始之前,系统建立了一个内存回收池,在这一帧的过程中,当我们调用了autorelease 方法以后,我们的对象就会放到这个内存回收池中,当一帧结束的时候这个内存回收池就会释放掉,这个时候在内存回收池中的对象就会被release 一下,也就是说引用计数就会减一,如果这个时候引用计数为0,就会删除对象了。如果引用计数不为0 的话对象是不会被删除的,下一帧开始的时候系统又会创建一个内存回收池,这个时候在上一次添加的对象这个时候是不会重新添加到这个内存回收池中的,在这个内存回收池中的对象是你在这一帧中调用了autorelease 函数的对象。
自动就是在这一帧结束的时候将对象开始new 的时候加的那个引用计数减掉,而让引擎中持有对象引用的其他类去管理这个对象,当持有者析构的时候就删除引用,引擎中的类负责retain 和release,这就是所谓的自动管理!