cocos2dx内存管理

cocos2dx基于引用计数管理内存,所有继承自CCObject的对象都将获得引用计数的能力,可通过调用retain成员函数用于引用计数值,调用release减少引用计数值,当计数值减为0时销毁对象.

cocos2dx的对象管理是树形结构的,可通过调用父亲节点的addChild成员函数将一个子节点对象添加到父节点中,当子节点被添加到父亲节点中,子节点的引用计数值加1,如果通过removeChild将子节点从父节点中移除子节点的引用计数值减1.当父节点被销毁时,会遍历其所有的子节点,将其子节点的引用计数值减1,当子节点的计数值为0,销毁子节点.

提到cocos2dx的内存管理,就不得不提autorelease.

我们随便查看一个函数:

CCSprite* CCSprite::createWithTexture(CCTexture2D *pTexture)
{
    CCSprite *pobSprite = new CCSprite();
    if (pobSprite && pobSprite->initWithTexture(pTexture))
    {
        pobSprite->autorelease();
        return pobSprite;
    }
    CC_SAFE_DELETE(pobSprite);
    return NULL;
}

  

注意到CCSprite被生成并成功初始化之后(initWithTexture)立刻就调用了autorelease函数.那么autorelease的作用到底是什么.首先当一个对象A被创建出来之后它的引用计数值必定为1,如果我们将A添加到一个父节点B中,此时它的引用计数值为2.之后销毁父节点B,依据上面的描述,父节点被销毁时会将其子节点的引用计数减1.也就是说父节点B被销毁之后A的引用计数值为1。那么我们就必须手动在不再需要A的时候调用releasedelete来将A销毁.为了减轻程序员的负担,cocos2dx提供了CCAutoreleasePool当节点A被添加到autoreleasepool之后,我们就不在需要关注A的销毁了.因为引擎会在每帧结束之后清理autoreleasepool中的对象,调用它的release如果如果对象引用计数值降为0将对象销毁.

下面是CCAutoreleasePool::addObject,autorelease最终调用的就是这个函数:

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

CCAutoreleasePool中添加一个对象,底层管理容器是一个array对象被添加到array的时候不检测重复性,所以个一对象可以被添加多次.添加完成后需要调用release函数减少引用计数,因为在array->addObject中调用了retain.

应当注意避免对一个object多次调用autorelease,对object调用一次autorelease将导致当前帧结束时对这个对象调用一次release,假设一个对象没被添加进任何容器,则其引用计数值为1,如果调用两次autorelease则当前帧结束时会调用两次release其中第二次必定是在一个已经被销毁的对象上执行的.而如果这一对象已经添加另外一个容器,则会导致那个容器在当前帧结束之后持有一个已经被销毁的对象指针.此问题在cocos2dx rc3中已经添加了assert处理,对象销毁时判断在pool是否有另外的实例存在,如果有则报错.虽然只在debug版本中处理,但也不用通过遍历vector这么低效的手段来判断重复实例的存在吧.难道2.2.3版本的m_uAutoReleaseCount就没有给开发人员提供一点启发?

我们来看看引擎的主循环:

void CCDisplayLinkDirector::mainLoop(void)
{
    if (m_bPurgeDirecotorInNextLoop)
    {
        m_bPurgeDirecotorInNextLoop = false;
        purgeDirector();
    }
    else if (! m_bInvalid)
     {
         drawScene();

         // release the objects
         CCPoolManager::sharedPoolManager()->pop();
     }
}

在每帧中调用drawScene(),之后就调用CCPoolManager::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);

//         if(nCount > 1)
//         {
//             m_pCurReleasePool = m_pReleasePoolStack->objectAtIndex(nCount - 2);
//             return;
//         }
        m_pCurReleasePool = (CCAutoreleasePool*)m_pReleasePoolStack->objectAtIndex(nCount - 2);
    }

    /*m_pCurReleasePool = NULL;*/
}

好了关键函数就是m_pCurReleasePool->clear(),这个函数完成了对象的清理过程.

在这里我们发现了一点奇怪的地方,为啥有个m_pReleasePoolStack还有个m_pCurReleasePool.

首先来看下ReleasePool对象是在哪里被创建的:

void CCPoolManager::push()
{
    //if(!m_pCurReleasePool){
        CCAutoreleasePool* pPool = new CCAutoreleasePool();       //ref = 1
        m_pCurReleasePool = pPool;

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

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

然后我们跟踪程序的运行将断点放在push函数里,断点的第一次命中调用栈如下:

 libcocos2d.dll!cocos2d::CCPoolManager::push() 行 145 C++
 libcocos2d.dll!cocos2d::CCPoolManager::getCurReleasePool() 行 200    C++
 libcocos2d.dll!cocos2d::CCPoolManager::addObject(cocos2d::CCObject * pObject=0x003e9938) 行 189  C++
 libcocos2d.dll!cocos2d::CCObject::autorelease() 行 100   C++
libcocos2d.dll!cocos2d::CCDictionary::create() 行 396    C++
 libcocos2d.dll!cocos2d::CCConfiguration::init() 行 60    C++
 libcocos2d.dll!cocos2d::CCConfiguration::sharedConfiguration() 行 154    C++
 libcocos2d.dll!cocos2d::CCDirector::setDefaultValues() 行 199    C++
 libcocos2d.dll!cocos2d::CCDirector::init() 行 113    C++
 libcocos2d.dll!cocos2d::CCDirector::sharedDirector() 行 97   C++
 IslandFight.exe!AppDelegate::applicationWillEnterForeground() 行 143 C++
 libcocos2d.dll!cocos2d::CCEGLView::WindowProc(unsigned int message=5, unsigned int wParam=0, long lParam=20972000) 行 444    C++
 libcocos2d.dll!cocos2d::_WindowProc(HWND__ * hWnd=0x002a06aa, unsigned int uMsg=5, unsigned int wParam=0, long lParam=20972000) 行 171   C++

第二次调用栈:

libcocos2d.dll!cocos2d::CCPoolManager::push() 行 145 C++
libcocos2d.dll!cocos2d::CCDirector::init() 行 165    C++
libcocos2d.dll!cocos2d::CCDirector::sharedDirector() 行 97   C++
IslandFight.exe!AppDelegate::applicationWillEnterForeground() 行 143 C++
libcocos2d.dll!cocos2d::CCEGLView::WindowProc(unsigned int message=5, unsigned int wParam=0, long lParam=20972000) 行 444    C++
libcocos2d.dll!cocos2d::_WindowProc(HWND__ * hWnd=0x002a06aa, unsigned int uMsg=5, unsigned int wParam=0, long lParam=20972000) 行 171   C++

也就是说总共创建了两个ReleasePool,ReleasePool都被添加到m_pReleasePoolStack成员中,这个成员是一个arraym_pCurReleasePool则指向m_pReleasePoolStack的最后一个成员.

接着我们将断点放到CCPoolManager::pop()中,可以发现第一次pop命中的时候nCount == 2,所以执行if(nCount > 1)之后的流程, 这个流程的处理就是将m_pReleasePoolStack的最后一个成员销毁,然后将m_pCurReleasePool指向m_pReleasePoolStack中最后一个成员.

继续观察程序的运行可以发现,在第一帧之后m_pReleasePoolStack中实际上永远只剩一个成员.我将

bool CCDirector::init(void)中的CCPoolManager::sharedPoolManager()->push()注释掉,程序依旧能正确的运行.

我搜索cocos2dx 2.2.3和cocos2dx 3.0rc源代码,并无找到必须启用多个ReleasePool的地方, 那么多ReleasePool存在的意义就仅剩调试了.

研究cocos2dx不久,文中有理解不当的地方欢迎指正.


cocos2dx内存管理,布布扣,bubuko.com

时间: 2024-12-15 02:17:38

cocos2dx内存管理的相关文章

cocos2dx 内存管理

cocos2dx的内存管理移植自Objective-C, 对于没有接触过OC的C++开发人员来说是挺迷惑的.不深入理解内存管理是无法写出好的C++程序的,我用OC和cocos2dx也有一段时间了,在此总结一下,希望对想用cocos2dx开发游戏的朋友有所帮助. C++的动态内存管理一般建议遵循谁申请谁释放的原则,即谁通过new操作符创建了对象,谁就负责通过delete来释放对象.如果对象的生命周期在一个函数内,这很容易做到,在函数返回前delete就行了.但一般我们在函数中new出来的对象的生命

cocos2d-x 源码分析 : Ref (CCObject) 源码分析 cocos2d-x内存管理策略

源码版本来自3.x,转载请注明 cocos2d-x 源码分析总目录: http://blog.csdn.net/u011225840/article/details/31743129 1.Ref,AutoreleasePool,PoolManager Ref中包含了一个叫referenceCount的引用计数,当一个Ref类的变量被new的时候,其referenceCount的引用计数被置为1. 其中有三个重要的操作,retain,release,autorelease,下面源码分析时会详细说明

Cocos2d-x内存管理

Cocos2d-x内存管理浅解 1.首先我们知道内存管理分为c++自身管理机制以及Cocos2d-x内存管理机制.在c++中,内存分为堆区.栈区.静态存储区(全局存储区).常量存储区.自由存储区. 主要先说一下堆区和栈区.堆区主要由new和malloc分配,new与delete,malloc与free成对出现,保证内存的分配与回收.堆内存分配地址是逐渐增大的,这一点与栈区相反,我们都知道栈是先进后出,所以栈的存储方向是内存地址逐渐减小的.栈中的内存也是系统自动回收的,这个我们不需要考虑自己管理内

帝国塔防2充值界面闪退BUG修复总结(cocos2dx内存管理机制)

游戏充值界面老是闪断,debug调试断点总是断在比较深的位置,也看不出哪里出错. 后来有一次断点断在了程序创建一个提示图片的地方,于是检查代码发现了问题. 修改之前的错误代码: 1.创建提示的代码: m_loading = new NetLoading(this, callfunc_selector(IapShopLayer::downingUpdate)); // m_loading 引用计数为1 m_loading->setPosition(ccp(1139/2.0f, 640.0f/2.0

2、COCOS2D-X内存管理机制

在C++中,动态内存分配是一把双刃剑,一方面,直接访问内存地址提高了应用程序的性能,与使用内存的灵活性:另一方面,由于程序没有正确地分配与释放造成的例如野指针,重复释放,内存泄漏等问题又严重影响着应用程序的稳定性. 人们尝试着不同的方案去避免这个问题,比较常用的如智能指针,自动垃圾回收等,这些要么影响了应用程序的性能,要么仍然需要依赖于开发者注意一些规则,要么给开发者带来了另外一些很丑陋的用法(实际上笔者很不喜欢智能指针).因此,优秀的C++内存管理方案需要兼顾性能,易用性,所以到目前为止C++

cocos2d-x内存管理机制解析(转载)

最近在看内存管理的源码,发现这篇文章讲的不错,思路很清晰,故转载收藏. 原地址:http://blog.csdn.net/a7833756/article/details/7628328 1.cocos2d-x 内存管理的方式,cocos2d-x采用引用计数的方式进行内存管理,当一个对象的引用计数为0的时候,就会被引擎自动delete掉. 所有cocos2d-x里面的类都继承ccobject类(应该是吧.),下面看ccobject类源码: 这里 m_uReference 就是引用计数,在对象构造

记录关于cocos2dx内存管理机制可能崩溃的一个坑

大年初一写代码,纪念一下:) cocos2dx,内存管理方式如下: 所有Ref继承而来的类,皆放入AutoreleasePool,每一帧释放一次,如果引用计数为0,则delete. 因此,Ref在每帧的工作,就是先ref+1,后ref-1,让它ref-1的是AutoreleasePool,而让它ref+1的就各有方式了.最常见的就是CCNode,每帧为子节点retain的方式. 一般这是不会有问题的.如果你有需求在类中保存一个Ref对象,而它并非一个子节点,如RenderTexture,则必须记

cocos2dx 内存管理的理解

关于引擎内存管理的细节,网上有大量的详解,这里概括一下: cocos2d-x 的世界是基于 CCObject 类构建的,所以内存管理的本质就是管理一个个 CCObject. //CCObject 内部维护着一个引用计数,引用计数为 0 就自动释放 unsigned int m_uReference; //管理内存的实质就是管理这些 “引用计数” 了,使用 retain 和 release 方法对引用计数进行操作 void release(void);//引用计数:--m_uReference v

cocos2d-x内存管理(见解)

cocos2d-x 延续了cocos2d 和OC的引用计数的内存管理机制! 下面我们来看看CCDriectro类 CCPoolManager::sharedPoolManager()->push(); 基础:我们要先熟悉  release  释放该内存! CCPoint *temp = new CCPoint(controlPoint.x, controlPoint.y); m_pControlPoints->addObject(temp); temp->release(); autor