zoukankan      html  css  js  c++  java
  • cocos2d-x回收池原理

     

    cocos2d-x源于cocos2d-iphone,为了与Objective-c一致,cocos2d-x也采用了引用计数与自动回收的内存管理机制。

    要现实自动内存回收,需继承于cocos2d-x的根类CCObject。当然自动释放会影响性能的。

     

    cocos2d-x中有很多静态工场方法,例如以create开头的,这些静态工场方法创建的对象都使用的autorelease,试想如果不用autorelease,

    CCObject* create(){CCObject *ret = new CCObject(); return ret;}

    此时函数内的引用在函数结束时就结束了,但是对方没有被释放,如果返回的ret没有被用户引用或者释放,那么就造成了内存泄露。所以加入autorelease是个不错的解决方法。

     

    看看CCObject的这两个字段和方法:

    protected:
        // 引用计数
        unsigned int        m_uReference;
        // 自动释放次数   以前是bool m_bManaged;标识是否采用自动释放
        unsigned int        m_uAutoReleaseCount;
    public:
        void release(void);
        void retain(void);
        CCObject* autorelease(void);
        unsigned int retainCount(void);

    有这么一个原则:谁引用谁retain和release,对象传值时先retain再release(避免自己给自己传值时,先release可能会释放对象),调用release方法,当引用计数为0时,就会delete此对象。

    内存管理autorelease是怎么实现的呢,进入autorelease看到调用了下面这个函数:

    CCPoolManager::sharedPoolManager()->addObject(this);将对象加入回收池

    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.
    }

    函数中将对象加入管理数组中,再对自动释放计数+1,然后release使引用计数-1。

    当create一个对象时,new使引用计数为1,调用autorelease后,加入回收池,然后release引用计数-1。那么此时引用计数为0吗?

    答案肯定不是,如果为0,此时该对象就会被delete。cocos2d-x中的数据结构转为这种内存管理设置,如CCArray,当对array调用addObject时,就会调用retain使引用计数加1,当array调用removeObject时就会release。那么当加入回收池后,此时的这个引用归回收池所有,当一帧结束释放回收池的时候,回收池中的所有对象都会调用release。如果此时没有其他对象引用该对象,则该对象会被删除。

     

    来看看主循环mainLoop中的调用:

        if (m_bPurgeDirecotorInNextLoop)
        {
            m_bPurgeDirecotorInNextLoop = false;
            purgeDirector();
        }
        else if (! m_bInvalid)
         {
             drawScene();
         
             // release the objects
             CCPoolManager::sharedPoolManager()->pop();        
         }

    每一帧结束调用sharedPoolManager()->pop()此时栈顶的回收池就被释放,

    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);
        }
    }

    此时池中的所有对象都会被release,如果没有其它的对象(场景、层、数组等)在引用他们,这些对象就会被delete。

    而新一帧开始后,第一个调用autorelease的对象在内部调用getCurReleasePool()时,如果栈中没有回收池,就会push一个回收池,sharedPoolManager()->push()。

    CCAutoreleasePool* CCPoolManager::getCurReleasePool()
    {
        if(!m_pCurReleasePool)
        {
            push();
        }
        CCAssert(m_pCurReleasePool, "current auto release pool should not be null");
        return m_pCurReleasePool;
    }

     

    可以看出每一帧回收池都要建立、删除,而且添加对象进入回收池,那么性能方面当然会受到影响,当回收池中对象很多时,表现得很明显。

    那么解决方法1:性能要求高的情况下不要轻易使用自动回收池。

     

    方法2:手动释放并创建一个回收池

    CCPoolManager::shardPoolManager()->push();
    for(int i=0; i!=n; ++i)
    {
         objArray[i]->autorelease();
    }
    CCPoolManager::shardPoolManager()->pop();

    这样就不会让自动释放的对象集中在一帧结束的时候。

     

     

  • 相关阅读:
    现代C++学习笔记之二入门篇2,数据转换
    现代C++学习笔记之二入门篇1
    现代C++学习笔记之一资料篇(C++ 11)
    OSG3.2+Qt5.2.1+VS2012+OSGEarth 2.5编译问题记录
    无法解析的外部符号 _WinMain@16(原)
    qt-vs-addin:Qt4和Qt5之VS插件如何共存与使用(转)
    一些webGL地球的网址
    Windows7下使用sphinx生成开源文档(原)
    VC解决方案,项目,开发一段时间启动调试很慢,半天才开始链接
    SQL笔记
  • 原文地址:https://www.cnblogs.com/songcf/p/3161532.html
Copyright © 2011-2022 走看看