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

cocos2d-x的内存管理机制是回收池和引用计数合用来进行管理,这套机制一直为人们所津津乐道。
C++并没有内存管理的机制,都是用new/delete来进行对象的管理(STL和boost倒是有智能指针)

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

现在先讲CCObject,CCOjbect继承于CCCopying

//CCObject.h
class CC_DLL CCCopying
{
public:
    virtual CCObject* copyWithZone(CCZone* pZone);
};
//CCObject.cpp
CCObject* 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.cpp
CCObject::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; //引用 = 1
	arr->addOjbect(object); //引用 = 2
	/*do something here*/
	object->release(); //引用 = 1,未被析构
	CCObject *test = arr->getObjectAtIndex(0); //可以访问
	arr->removeObjectAtIndex(0); //引用 = 0,析构
}

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

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

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

//CCAutoreleasePool.h
class 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.h
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;
};

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


内存管理在pop函数中体现

//CCAutoreleasePool.cpp
void 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进行生成
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值