您的位置:首页 > 移动开发 > Cocos引擎

Cocos2d-x 内存管理剖析(1)

2013-11-04 14:14 274 查看
下面这篇文章将深入cocos2dx引擎的源代码,剖析cocos2dx中内存的管理机制的实现。使用cocos2dx-2.1.5版本。

参考文章:点击打开链接 

一、前言:

我们都知道在c++中,创建实例化的对象都是使用new关键字,在不使用或者不需要的时候必须手动使用delete关键字去掉这个对象,否则new出来的对象就不能回收,造成内存泄露。

例如(我们创建了一个大小为10的数组)

[cpp] view
plaincopy

int* p = new int[10];  

    for (int i = 0; i<10; i++) {  

        p[i] = i;  

    }  

如果使用结束后,不手动 delete [] p; 那么就会造成这个数组的内存泄露。

二、Cocos2d-x采用引用计数方式对对象进行内存管理

关于引用计数方式的内存管理,熟悉ios开发的人来说应该不陌生;其内存管理的过程大致是:为每一个对象保存一个引用计数,当引用计数大于0的时候,对象继续保存在内存中;但是当引用计数等于0的时候,自动释放这个对象,这个过程就是autorelease(自动管理)。

在Cocos2d-x中,CCObject是整个引擎的基础类,其他的类都是继承自这个类而来。

那么,首先从这个类开始进行内存管理的剖析:

在 libs/cocos2dx/cocoa/ 位置下找到这个类文件。

[cpp] view
plaincopy

class CC_DLL CCObject : public CCCopying  

{  

public:  

    // object id, CCScriptSupport need public m_uID  

    unsigned int        m_uID;  

    // Lua reference id  

    int                 m_nLuaID;  

protected:  

    // count of references  

    unsigned int        m_uReference;  

    // count of autorelease  

    unsigned int        m_uAutoReleaseCount;  

public:  

    CCObject(void);  

    virtual ~CCObject(void);  

      

    void release(void);  

    void retain(void);  

    CCObject* autorelease(void);  

    CCObject* copy(void);  

    bool isSingleReference(void) const;  

    unsigned int retainCount(void) const;  

    virtual bool isEqual(const CCObject* pObject);  

  

    virtual void acceptVisitor(CCDataVisitor &visitor);  

  

    virtual void update(float dt) {CC_UNUSED_PARAM(dt);};  

      

    friend class CCAutoreleasePool;  

};  

说明:

public 中的两个属性:对象的ID编号(其中第二个是用于Lua中的);

protected 中的两个属性:

  m_uReference表示对象的引用计数;

  m_uAutoReleaseCount 则表示对象是否自动管理,也就是表示是否autorelease。注意到 m_uAutoReleaseCount 这个变量是unsigned int类型,在之前一些版本中,曾经是使用 bool m_bManaged 这个bool变量,其实本质是一样的,如果对象autorelease(自动管理),那么
++m_uAutoReleaseCount,只要这个值非零,那么这个对象就是autorelease的。而假如是bool变量的话,那么就是让其变成true就是autorelease了。

前面说到,对对象的内存管理是使用到引用计数,那么我们就看看CCObject中,引用计数 m_uReference 是如何变化的吧!

首先我们进入CCObject的构造方法中:

[cpp] view
plaincopy

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;  

}  

显然,创建一个CCObject对象之后,其引用计数 m_uReference 变为1,而 m_uAutoReleaseCount 为0,表示对象还没有autorelease。

接着就是retain和release方法了,前者对引用计数加1,后者引用计数减1。

[cpp] view
plaincopy

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;  

}  

其中release方法中,如果引用计数 m_uReference 为0,那么就自动删除这个对象进行回收。

好了,通过上面的介绍我们就可以知道cocos2dx中的引用计数是怎么一回事了,接下来就是 :引擎中是如何对实例对象进行自动管理。

三、实例对象自动管理autorelease

下面从一个CCNode创建的生命历程来讲一下这个自动管理的过程。

在 libs/cocos2dx/base_nodes 中找到CCNode的类文件。

进入其中的create方法。

[cpp] view
plaincopy

CCNode * CCNode::create(void)  

{  

    CCNode * pRet = new CCNode();  

    if (pRet && pRet->init())  

    {  

        pRet->autorelease();  

    }  

    else  

    {  

        CC_SAFE_DELETE(pRet);  

    }  

    return pRet;  

}  

其中 create 创建的过程是先用 new创建对象,然后将对象自动管理 autorelease。(所以我们就知道了,我们一般创建节点对象都使用create,其过程就是一个new+autorelease
= create)

进入其中的autorelease方法

[cpp] view
plaincopy

CCObject* CCObject::autorelease(void)  

{  

    CCPoolManager::sharedPoolManager()->addObject(this);  

    return this;  

}  

进入其中的addObject方法

[cpp] view
plaincopy

void CCPoolManager::addObject(CCObject* pObject)  

{  

    getCurReleasePool()->addObject(pObject);  

}  

再进入其中的addObject方法

[cpp] view
plaincopy

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.  

}  

首先看一下在这个过程中我们遇到了这两个类:CCPoolManager 和 CCAutoreleasePool,前者是对象自动管理类,后者是对象自动管理(释放)池。后面再详细分析一下这个autorelease过程。

先看到CCAutoreleasePool这个类:

[cpp] view
plaincopy

class CC_DLL CCAutoreleasePool : public CCObject  

{  

    CCArray*    m_pManagedObjectArray;      

public:  

    CCAutoreleasePool(void);  

    ~CCAutoreleasePool(void);  

  

    void addObject(CCObject *pObject);  

    void removeObject(CCObject *pObject);  

  

    void clear();  

};  

类中有一个 m_pManagedObjectArray 数组,我们将对象自动管理后,都是将对象添加到这个数组中的,所以说是对象自动管理(释放)池。值得注意的是,这个类也是继承自CCObject,那么显然其内存管理形式也采用引用计数的。

其中的addObject方法就是将需要自动管理的对象添加到这个m_pManagedObjectArray数组中,那么就说将对象进行了自动管理autorelease。

下面进入这个方法看看:

[cpp] view
plaincopy

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.  

}  

首先是将对象添加到数组中,然后将对象自动管理变量++,那么对象的 m_uAutoReleaseCount =1,也就是表明其为自动管理对象。

然后对对象进行release,也即对对象的引用计数进行减1的操作,这个是为什么呢?

下面分析一个这个原因:

①首先一个CCNode对象被创建,使用的是new,也就是调用了构造方法,那么其引用计数 m_uReference=1。这个应该不难理解吧:CCNode是继承自CCObject,而在前面CCObject的构造方法中就知道,new创建一个实例对象后,其引用计数m_uReference=1,而是否自动管理变量 m_uAutoReleaseCount=0(表示对象未添加到自动管理(释放)池中自动管理)。

②接着对象autorelease,那么就是将对象添加到自动释放池中,而其中的:

m_pManagedObjectArray->addObject(pObject);
将被管理对象添加到自动管理池中的过程中(将对象添加到数组中),其会对对象进行retain的,所以 m_uReference 就变成了 m_uReference=2。

下面跟踪一下这个过程:

[cpp] view
plaincopy

void CCArray::addObject(CCObject* object)  

{  

    ccArrayAppendObjectWithResize(data, object);  

}  

[cpp] view
plaincopy

/** Appends an object. Capacity of arr is increased if needed. */  

void ccArrayAppendObjectWithResize(ccArray *arr, CCObject* object)  

{  

    ccArrayEnsureExtraCapacity(arr, 1);  

    ccArrayAppendObject(arr, object);  

}  

[cpp] view
plaincopy

/** Appends an object. Behavior undefined if array doesn't have enough capacity. */  

void ccArrayAppendObject(ccArray *arr, CCObject* object)  

{  

    CCAssert(object != NULL, "Invalid parameter!");  

    object->retain();  

    arr->arr[arr->num] = object;  

    arr->num++;  

}  

看到最后方法中 object->retain(); 了吧!

③所以最后的release是为了将引用计数重新变成为1。这样的话,从对象创建,到被放到对象自动管理池,引用计数依然为1,被管理值也为1(表征对象是自动管理)。

下面看看 CCPoolManager 类,这个类顾名思义就知道是对对象自动释放池进行管理的类,它是一个单例类。

[cpp] view
plaincopy

class 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;  

};  

这个类中的属性 m_pCurReleasePool
表示当前的自动释放池,可以使用 getCurReleasePool()这个方法获取到当前的这个属性,进入这个方法:

[cpp] view
plaincopy

CCAutoreleasePool* CCPoolManager::getCurReleasePool()  

{  

    if(!m_pCurReleasePool)  

    {  

        push();  

    }  

  

    CCAssert(m_pCurReleasePool, "current auto release pool should not be null");  

  

    return m_pCurReleasePool;  

}  

这个类中还有一个属性m_pReleasePoolStack 属于CCArray类型,表示对象自动释放池数组,其中可以有多个对象自动释放池。

比如说,在上面的 getCurReleasePool() 方法中,如果 m_pCurReleasePool 为空,那么就要进入push()方法创建一个对象自动释放池,并且添加到m_pReleasePoolStack中。

[cpp] view
plaincopy

void CCPoolManager::push()  

{  

    CCAutoreleasePool* pPool = new CCAutoreleasePool();       //ref = 1  

    m_pCurReleasePool = pPool;  

  

    m_pReleasePoolStack->addObject(pPool);                   //ref = 2  

  

    pPool->release();                                       //ref = 1  

}  

如代码所示,push()操作 new 了一个自动释放池对象,并且将它赋值给 当前自动释放池(m_pCurReleasePool),然后将这个new的自动释放池对象放到CCPoolManager 里面的自动释放池数组中。

注意过程中对其引用计数的控制,自动释放池本身也是继承ccobject的,也有自己的引用计数,受相同的内存管理方式。其中的注释就表明了自动释放池对象的引用计数。

好,大致已经介绍了CCPoolManager 和 CCAutoreleasePool 这两个类,那么再来看将对象自动管理autorelease的过程就比较简单了。

首先调用构造方法,new一个对象(我们称之为object),然后这个对象调用autorelease方法,接着就是得到

CCPoolManager的单例变量,用getCurReleasePool()
方法获取到当前的对象自动释放池对象(如果没有的话,就新建一个对象自动释放池对象,并且添加到 对象自动释放池数组中),然后把这个object对象添加到自动释放池中的

m_pManagedObjectArray 数组中,执行完这个过程,这个新建的object对象的引用计数值为1,被管理状态值也为1(表征该对象是自动管理的)。

下面通过一个简单的例子来说明上面所讲述的内容:

1、new创建一个对象,还没有autorelease。





2、对对象进行autorelease操作





3、对对象进行retain操作:



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  内存管理 cocos2d-x