本文出自[无间落叶](转载请保留出处):http://blog.leafsoar.com/archives/2013/06-04-10.html

  如果 Cocos2d-x 内存管理浅说 做为初步认识,而 Cocos2d-x 内存管理的一种实现做为进阶使用,那么本文将详细的分析一下 Cocos2d-x 的内存管理的设计实现和原理。知其然,知其所以然 ~或者说:嗯,它这么做,一定是有原因的,体会设计者的用意,感同身受,如果是你,将会如何设计!~~

  我觉得 最好的学习方式是以自己的语言组织,说与别人听 ~ 这样对自己:更容易发现平时容易忽略的问题,对别人:或多或少也有所助益!以学习为目的,而别人的受益算是附带的效果,这样一个出发点 ~

  由浅入深,总览全局(或者由整体到局部)是我喜欢的出发点,或者思考角度,我不喜欢拘泥于细节的实现,因为那会加大考虑问题的复杂度,所以 把复杂的问题简单化,是必然的过程。 那么本文就说说 Cocos2d-x 的架构是如何设计以方便内存管理的。从理论到实践 ~(当然是从我看问题的角度 ,读者如有异议,欢迎讨论!文本使用 cocos2d-x 2.0.4 解说。)
 



引用计数的由来

  cocos2d-x 的世界是基于 CCObject 类构建的,其中的每个元素:层、场景、精灵等都是一个个 CCObject 的对象。所以 内存管理的本质就是管理一个个 CCObject。作为一个 cocos2d 的 C++ 移植版本,在它之前有很多语言的 实现,从架构层次来说,这与语言的实现无关(比如 CCNode 的节点树形关系,语言也可以实现,如果是内存方便,C# 等更是无需考虑),但就从内存管理方面来说,参考了 OC (Objective-C) 的内存管理实现。

  一个简单的自动管理原则:CCObject 内部维护着一个引用计数,引用计数为 0 就自动释放 ~(如果么有直接做如 delete 之类的操作)。那么此时可以预见,管理内存的实质就是管理这些 引用计数 了!使用 retain 和 release 方法对引用计数进行操作!

 

 



为什么要有自动释放池 及其作用

  我们知道 cocos2d-x 使用了自动释放池,自动管理对象,知其然!其所以然呢?为什么需要自动释放池,它在整个框架之中又起着什么样的作用!在了解这一点之前,我们需要 知道 CCObject 从创建之初,到最终销毁,经历了哪些过程。在此,一叶总结以下几点:

 

  • 刚创建的对象,而 为了保证在使用之前不会释放(至少让它存活一帧),所以自引用(也就是初始为1)
  • 为了确定是否 实际使用,所以需要在一个合适的时机,解除自身引用。
  • 而这个何时的时机正是在帧过度之时。
  • 帧过度之后的对象,用则用矣,不用则弃!
  • 由于已经解除了自身引用,所以它的引用被使用者管理(一般而言,内部组成树形结构的链式反应,如 CCNode)。
  • 链式反应,也就是,如果释放一个对象,也会释放它所引用的对象。

  上面是一个对象的大致流程,我们将对象分为两个时期,一个是刚创建时期,自引用为 1(如果为 0 就会释放对象,这是基本原则,所以要大于 0) 的时期,另一个是使用时期。上面说到,为了保证创建时期的对象不被销毁,所以自引用(并没有实际的使用)初始化为 1,这就意味着我们需要一个合适的时机,来解除这样的自引用。

  何时?在帧过度之时!(这样可保证当前帧能正确使用对象而没有被销毁。)怎么样释放?由于是自引用,我们并不能通过方式访问到它,所以就有了自动释放池,我们 变相的将“自引用”转化“自动释放池引用”,来标记一个 “创建时期的对象”。然后在帧过度之时,通过自动释放池管理,统一释放 “释放池引用”,也就意味着,去除了“自身引用”。帧过度之后的对象,才是真正的被使用者所管理。 下面我们用代码来解释上述过程。

  通常我们使用 create(); 方法来创建一个自动管理的对象,而其内部实际操作如下:

 

 

  1. // 初始化一个对象
  2. static CCObject* create()
  3. {
  4. // new CCObject 对象
  5. CCObject *pRet = new CCObject();
  6. if (pRet pRet->init())
  7. {
  8. // 添加到自动释放池
  9. pRet->autorelease();
  10. return pRet;
  11. }
  12. else
  13. {
  14. delete pRet;
  15. pRet = 0;
  16. return 0;
  17. }
  18. }
  19.  
  20. // 我们看到初始化的对象 自引用 m_uReference = 1
  21. CCObject::CCObject(void)
  22. :m_uAutoReleaseCount(0)
  23. ,m_uReference(1) // when the object is created, the reference count of it is 1
  24. ,m_nLuaID(0)
  25. {
  26. static unsigned int uObjectCount = 0;
  27.  
  28. m_uID = ++uObjectCount;
  29. }
  30.  
  31. // 标记为自动释放对象
  32. CCObject* CCObject::autorelease(void)
  33. {
  34. // 添加到自动释放池
  35. CCPoolManager::sharedPoolManager()->addObject(this);
  36. return this;
  37. }
  38.  
  39. // 继续跟踪
  40. void CCPoolManager::addObject(CCObject* pObject)
  41. {
  42. getCurReleasePool()->addObject(pObject);
  43. }
  44.  
  45. // 添加到自动释放池的实际操作
  46. void CCAutoreleasePool::addObject(CCObject* pObject)
  47. {
  48. // 内部是由一个 CCArray 维护自动释放对象,并且此操作 会使引用 + 1
  49. m_pManagedObjectArray->addObject(pObject);
  50.  
  51. // 由于初始化 引用为 1,上面又有操作,所以引用至少为 2 (可能还被所引用)
  52. CCAssert(pObject->m_uReference > 1, reference count should be greater than 1);
  53. ++(pObject->m_uAutoReleaseCount);
  54. // 变相的将自身引用转化为释放池引用,所以减 1
  55. pObject->release(); // no ref count, in this case autorelease pool added.
  56. }
  • // 如果这里不 retain则以后就用不到了
    • lu->m_sSprite->retain();
      •  
      • // 方式二:使用方法
        • LUser* lu = LUser::create();
          • lu->m_sUserName = 一叶;
            • // 这里的 sprite 会随着 lu 的消亡而消亡,不用管释放问题了
              • lu->setSprite(CCSprite::create(a.png));
              • 复制代码
                  我们看到方式二相比方式一的设计,它通过 setSprite 内部对 sprite 本身 retain,从而实现链式反应,而不是直接使用 lu->m_sSprite->retain();,这样的好处是,我只要想着释放 LUser,而不用考虑LUser 内部 sprite 的引用情况就行了。如此才能把 cocos2d-x 内存的自动管理特性完全发挥 ~

                  而要实现这样管理的一个明显特征就是,隐藏 retain 和 release 操作 ~

                 

                  稍作总结

                  关于 cocos2d-x 的内存管理从使用到原理,系列文章就到这里了!(三篇也算系列 = =!) 由表象到内部的思考探索过程,其实在 浅说 当中对 cocos2d-x 的使用,便已经能够知晓内部细节设计之一二,透过现象看本质!三篇文章包含了,使用浅说(简单的测试),一种防止内存泄漏的设计(加强链式反应),最后纵览 cocos2d-x 的内存管理框架,对 CCObject 的生命周期做了简单的说明,当然其中还是隐藏一些细节的,比如管理都是用 CCArray 来管理,但我们并没有对 CCArray 做介绍,它是如何添加元素,如何引用等。在任何时候我们只针对一个问题进行思考,那我们该把 CCArray 这样的辅助工具类放在何处,如果你了解当然最好,不过不了解,那便 存疑 ,然后对相应的问题,分而治之 ~

                  存疑 可以帮助一叶在某个时刻只针对某一个问题进行思考,从而使问题变的简单。对文中所涉及的到的两个类 CCPoolManager和 CCAutoreleasePool 其中所有的方法并没有面面俱到,当然有了整体思路,去 填充那些 小疑问将会变得简单。
                锐亚教育

                锐亚教育,游戏开发论坛|游戏制作人|游戏策划|游戏开发|独立游戏|游戏产业|游戏研发|游戏运营| unity|unity3d|unity3d官网|unity3d 教程|金融帝国3|8k8k8k|mcafee8.5i|游戏蛮牛|蛮牛 unity|蛮牛