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 > , "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 > )
{
m_pReleasePoolStack->removeObjectAtIndex(nCount-); // if(nCount > 1)
// {
// m_pCurReleasePool = m_pReleasePoolStack->objectAtIndex(nCount - 2);
// return;
// }
m_pCurReleasePool = (CCAutoreleasePool*)m_pReleasePoolStack->objectAtIndex(nCount - );
} /*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() 行  C++
libcocos2d.dll!cocos2d::CCPoolManager::getCurReleasePool() 行 C++
libcocos2d.dll!cocos2d::CCPoolManager::addObject(cocos2d::CCObject * pObject=0x003e9938) 行 C++
libcocos2d.dll!cocos2d::CCObject::autorelease() 行 C++
libcocos2d.dll!cocos2d::CCDictionary::create() 行 C++
libcocos2d.dll!cocos2d::CCConfiguration::init() 行 C++
libcocos2d.dll!cocos2d::CCConfiguration::sharedConfiguration() 行 C++
libcocos2d.dll!cocos2d::CCDirector::setDefaultValues() 行 C++
libcocos2d.dll!cocos2d::CCDirector::init() 行 C++
libcocos2d.dll!cocos2d::CCDirector::sharedDirector() 行 C++
IslandFight.exe!AppDelegate::applicationWillEnterForeground() 行 C++
libcocos2d.dll!cocos2d::CCEGLView::WindowProc(unsigned int message=, unsigned int wParam=, long lParam=) 行 C++
libcocos2d.dll!cocos2d::_WindowProc(HWND__ * hWnd=0x002a06aa, unsigned int uMsg=, unsigned int wParam=, long lParam=) 行 C++

第二次调用栈:

libcocos2d.dll!cocos2d::CCPoolManager::push() 行  C++
libcocos2d.dll!cocos2d::CCDirector::init() 行 C++
libcocos2d.dll!cocos2d::CCDirector::sharedDirector() 行 C++
IslandFight.exe!AppDelegate::applicationWillEnterForeground() 行 C++
libcocos2d.dll!cocos2d::CCEGLView::WindowProc(unsigned int message=, unsigned int wParam=, long lParam=) 行 C++
libcocos2d.dll!cocos2d::_WindowProc(HWND__ * hWnd=0x002a06aa, unsigned int uMsg=, unsigned int wParam=, long lParam=) 行 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内存管理的更多相关文章

  1. cocos2d-x内存管理

    Cocos2d-x内存管理 老师让我给班上同学讲讲cocos2d-x的内存管理,时间也不多,于是看了看源码,写了个提纲和大概思想 一.   为什么需要内存管理 1. new和delete 2. 堆上申 ...

  2. cocos2dx内存管理的一些看法

    今年年初进入一家游戏公司,正式开始游戏引擎的学习,之前的ios学习,对现在的游戏引擎学习还是有很大的帮助的,虽然使用c++,但却能时刻感受到ios框架对于cocos2dx的巨大影响. 由于之前一直使用 ...

  3. 2、COCOS2D-X内存管理机制

    在C++中.动态内存分配是一把双刃剑,一方面,直接訪问内存地址提高了应用程序的性能,与使用内存的灵活性.还有一方面.因为程序没有正确地分配与释放造成的比如野指针,反复释放,内存泄漏等问题又严重影响着应 ...

  4. cocos2dx内存管理机制

    参考以下两篇文章 http://blog.csdn.net/ring0hx/article/details/7946397 http://blog.csdn.net/whuancai/article/ ...

  5. cocos2d-x内存管理(见解)

    cocos2d-x 延续了cocos2d 和OC的引用计数的内存管理机制! 下面我们来看看CCDriectro类 CCPoolManager::sharedPoolManager()->push ...

  6. cocos2d-x 内存管理浅析

    Cocos2d-x用create创建对象, 这个方法已经被引擎封装成一个宏定义了:CREATE_FUNC, 下面是这个宏定义的实现: #define CREATE_FUNC(__TYPE__) \   ...

  7. cocos2dx 内存管理的理解

    关于引擎内存管理的细节,网上有大量的详解,这里概括一下: cocos2d-x 的世界是基于 CCObject 类构建的,所以内存管理的本质就是管理一个个 CCObject. //CCObject 内部 ...

  8. cocos2dx 内存管理

    转载自 ocos2dx 内存管理 - 小花原创博客 - 博客频道 - CSDN.NET http://blog.csdn.net/ring0hx/article/details/7946397 coc ...

  9. Cocos2d-x内存管理研究<二>

    http://hi.baidu.com/tzkt623/item/46a26805adf7e938a3332a04   上一篇我们讲了内核是如何将指针加入管理类进行管理.这次我将分析一下内核是如何自动 ...

  10. Cocos2d-X内存管理研究<一>

    http://hi.baidu.com/tzkt623/item/651ca7d7a0aff6e055347f67        半夜没事干,研究内核,作为我cocos2d-x的第一篇教程.cocos ...

随机推荐

  1. Android Developers:传感器概述

    大 多数Android设备有内置的传感器,来测量运动,方向和各种环境条件.这些传感器能提供高精度和准确度的原始数据,如果你想监控设备三维运动或者位 置,或者你想监控设备周围的环境变化,是非常有用的.例 ...

  2. .Net性能的方方面面(必看官方经典)

    更多性能提高相关文章,必看 https://msdn.microsoft.com/en-us/library/hh917314.aspx Chapter 1 - Fundamentals of Eng ...

  3. 初识zookeeper(1)之zookeeper的安装及配置

    初识zookeeper(一)之zookeeper的安装及配置 1.简要介绍 zookeeper是一个分布式的应用程序协调服务,是Hadoop和Hbase的重要组件,是一个树型的目录服务,支持变更推送. ...

  4. [c#基础]ICloneable接口

    摘要 该接口使你能够创建现有对象的副本的自定义的实现.该接口只提供了,一个Clone方法,实现对象的浅拷贝.有浅拷贝,那么就有相对应的深拷贝.但该接口并没有对我们提供,需要我们自己实现. 什么是浅拷贝 ...

  5. Linux系统中安装使用百度云网盘

    百度云没有Linux客户端,于是有大神用Go语言写出来一个叫BaiduPCS-Go的命令行盘客户端,可以通过终端操作百度云盘,在Linux上实现上传下载.但是因为是命令行版本的,对没有命令行使用基础的 ...

  6. SQLSERVER 设置默认值

    DECLARE @test intSET @test=nullselect isnull(@test,0)

  7. 微软BI 之SSIS 系列 - 在 SSIS 中使用 Web Service 以及 XML 解析

    开篇介绍 Web Service 的用途非常广几乎无处不在,像各大门户网站上的天气预报使用到的第三方 Web Service API,像手机客户端和服务器端的交互等都可以通过事先设计好的 Web Se ...

  8. 【PMP】项目目标的SMART原则

    详细解读 Specific 具体的 用具体的语言清楚的说明要达成的标准. Measureable 可测量的 目标应该是明确的,而不是模糊的.应该有一组明确的数据,作为衡量是否达成目标的依据. Achi ...

  9. Reg 命令修改注册表

    首先要说明:编辑注册表不当可能会严重损坏您的系统.在更改注册表之前,应备份计算机上任何有价值的数据 只有在别无选择的情况下,才直接编辑注册表.注册表编辑器会忽略标准的安全措施,从而使得这些设置会降低性 ...

  10. 【转】[Network] 计算机网络基础知识总结

    阅读目录 1. 网络层次划分 2. OSI七层网络模型 3. IP地址 4. 子网掩码及网络划分 5. ARP/RARP协议 6. 路由选择协议 7. TCP/IP协议 8. UDP协议 9. DNS ...