cocos2d-x源码分析::内存管理机制

来源:互联网 发布:软件制作 编辑:程序博客网 时间:2024/06/05 20:40
cocos2d-x的内存管理机制是回收池和引用计数合用来进行管理,这套机制一直为人们所津津乐道。
C++并没有内存管理的机制,都是用new/delete来进行对象的管理(STL和boost倒是有智能指针)

现在让我来介绍 一下cocos2d-x的内存管理机制。
cocos2d-x的内存管理机制用到的类有CCObject, CCAutoreleasePool, CCPoolManager这几个类

现在先讲CCObject,CCOjbect继承于CCCopying

//CCObject.hclass CC_DLL CCCopying{public:    virtual CCObject* copyWithZone(CCZone* pZone);};//CCObject.cppCCObject* CCCopying::copyWithZone(CCZone *pZone){    CC_UNUSED_PARAM(pZone);    CCAssert(0, "not implement");    return 0;}

CCCopying其实是个接口,里面并没有任何实现。这里说一下宏命令CC_UNUESD_PARAM(),查看他的定义,很容易发现
#define CC_UNUSED_PARAM(unusedparam) (void)unusedparam
就是说这个宏完全没有执行任何命令,这样写的原因主要是历史遗留原因,ojb-c不存在纯虚函数并且传入参数不使用编译器会发出警告,这样用一个宏既可以防止警告也有一定的解释作用

CCCopying并没有父类,这样说来CCObject算是继承树上比较高的结点了,它负责了内存管理部分的引用计数
//CCObject.cppCCObject::CCObject(void):m_uAutoReleaseCount(0),m_uReference(1) // 构造时,引用数置为1,m_nLuaID(0){    static unsigned int uObjectCount = 0;    m_uID = ++uObjectCount;}void CCObject::release(void){    CCAssert(m_uReference > 0, "reference count should greater than 0");    --m_uReference;    if (m_uReference == 0)    {        delete this;    }}void CCObject::retain(void){    CCAssert(m_uReference > 0, "reference count should greater than 0");    ++m_uReference;}

可以看到,构造函数把引用数置为1,retain会把引用数加1,相反release会减1,每次release之后检测引用数,为0的话会析构当前对象。
另外cocos2d-x封装了一些容器,CCArray和CCDictionnary等,这些都是CCObject的容器,每当这些容器增加元素时,会调用一次retain,移除时会进行release

我们可以来对比一下下面的代码看一下引用计数和C++的new/delete的区别和优点

void function() {int *arr[10];int *num = new int(10);arr[0] = num;/*do something here*/                   delete num; int test = arr[0];/*arr[0]已经丢失*/}void function() {CCArray *arr = CCArray::createWithCapacity(10);CCObject *object = new CCObject; //引用 = 1arr->addOjbect(object); //引用 = 2/*do something here*/object->release(); //引用 = 1,未被析构CCObject *test = arr->getObjectAtIndex(0); //可以访问arr->removeObjectAtIndex(0); //引用 = 0,析构}

通过对比很容易发现两者不一样,通过引用计数,release和delete不同,release并未等于析构,当所有对象都不持有该对象的指针时,才析构该对象,下面,我们再看看CCObject的另一个关于内存管理的函数,autorealease

//CCObject.cppCCObject* CCObject::autorelease(void){    CCPoolManager::sharedPoolManager()->addObject(this);    return this;}

autorelease并未对引用进行任何的操作,只见他放进了CCPoolManager里面,这个其实就是内存池的管理者,那么我们先来看一下内存池是怎样的

//CCAutoreleasePool.hclass CC_DLL CCAutoreleasePool : public CCObject{    CCArray*    m_pManagedObjectArray;    public:    CCAutoreleasePool(void);    ~CCAutoreleasePool(void);    void addObject(CCObject *pObject);    void removeObject(CCObject *pObject);    void clear();};

内存池其实就是一个CCArray的封装,代码很简单,这里就不贴源文件了,不过这里要注意几点
1.CCAutorelease是CCObject的子类,所以也是要进行引用计数等的管理的
2.尽管addObject和removeObject是向一个CCArray添加对象,可是内存池进行了一些操作,这两个操作并不会改变对象的引用数,也就是说,加入内存池并不改变对象的引用数

3.clear()就是内存池进行清理,会对内部所有的CCObject执行一次release()


那么CCObject的autorelease的auto体现在哪里呢,取决于CCAutoreleasePool调用clear的时机,我们可以看一看CCPoolManager

//CCAutoreleasePool.hclass CC_DLL CCPoolManager{    CCArray*    m_pReleasePoolStack;        CCAutoreleasePool*                    m_pCurReleasePool;    CCAutoreleasePool* getCurReleasePool();public:    CCPoolManager();    ~CCPoolManager();    void finalize();    void push();    void pop();    void removeObject(CCObject* pObject);    void addObject(CCObject* pObject);    static CCPoolManager* sharedPoolManager();    static void purgePoolManager();    friend class CCAutoreleasePool;};

很高兴又见到了一个没有父类的家伙,从他的sharedPoolManager这个函数看来,他还是一个单件类,那就是名副其实的大哥类


内存管理在pop函数中体现

//CCAutoreleasePool.cppvoid CCPoolManager::pop(){    if (! m_pCurReleasePool)    {        return;    }    int nCount = m_pReleasePoolStack->count();    m_pCurReleasePool->clear();       if(nCount > 1)      {        m_pReleasePoolStack->removeObjectAtIndex(nCount-1);        m_pCurReleasePool = (CCAutoreleasePool*)m_pReleasePoolStack->objectAtIndex(nCount - 2);    }}

CCPoolManager就是一个CCAutorelease的栈,每次pop都把栈顶的内存池进行处理,把栈顶内存池管理的元素release掉。至于pop在什么时候调用?看过我写的主流程(mainloop)分析可以知道——
CCDiretor在mainloop()中调用了drawScene()和CCPoolManager::sharedPoolManager()->pop()
也就是说在每帧结束后,就会进行内存的管理


总结来说,cocos2d-x内存管理的引用计数部分通过retain/release来进行计数增减,而内存池管理就通过autorelease来进行管理,在每帧结束时自动回收


最后提一句,cocos2d-x的宏CREATE_FUNC(className)会自动生成一个静态的create函数,把该类生成,然后自动调用init()和autorelease()

包括cocos2d-x的一些类的create函数都是如此实现的,但autorelease会增加程序对内存管理的消耗,所以一般来说最好不要使用create进行生成