在上一节“Cocos2d-x 2.0 TestCpp框架源码分析”中,我们深入分析了TestController类的实现。其中我们曾经遇到这两句代码:
//新创建一个TestController对象
CCLayer* pLayer = newTestController();
//将它交由内存管理器进行内存释放管理
pLayer->autorelease();
在创建了一个TestController实例对象后,对它调用了autorelease函数。这个该怎么理解这一句的含义呢?我们先来看一下基类CCObject是怎么定义的。
读前小提示:CCScriptEngineManager是脚本引擎管理器,它对于使用的脚本引擎进行管理。说起脚本引擎,首先要了解什么是脚本?脚本是使用特定的语言格式来编写的描述一定逻辑功能的文本文件,它可以实现在文本文件中通过访问特定格式的函数对系统逻辑进行编程。这个文本文件不被编译进可执行程序中,而是动态的在需要的时候通过系统对这个文本文件的解释来执行。脚本引擎则可以理解为对脚本的使用管理进行封装的相关的类和接口函数。游戏中常用的脚本有Lua和Python。目前Cocos2d-x集成了Lua的脚本引擎模块,使用非常方便。后面的Lua章节我们进行详细讲解。
读前小提示:引用计数器是一种内存管理方式。通过一个无符号的成员变量计算当前有多少使用者在使用本内存。每次外部对象使用本内存时计数器加1,使用完要释放本内存时计数器减1,当计数器减为0时才真正进行占用内存释放。这样做可以实现在多个使用者使用一块内存时,只有当所有的使用者都确定不再使用这块内存的时候才进行内存的释放。避免了在还有使用者在使用内存时提前释放内存而导致的程序崩溃。
重点函数:release()
CCObject.h:
- #ifndef __CCOBJECT_H__
- #define __CCOBJECT_H__
-
- #include "platform/CCPlatformMacros.h"
-
- NS_CC_BEGIN
-
-
- class CCZone;
- class CCObject;
- class CCNode;
- class CCEvent;
-
-
- class CC_DLL CCCopying
- {
- public:
-
- virtual CCObject* copyWithZone(CCZone* pZone);
- };
-
- class CC_DLL CCObject : public CCCopying
- {
- public:
-
- unsigned int m_uID;
-
- int m_nLuaID;
- protected:
-
- unsigned int m_uReference;
-
- bool m_bManaged;
- public:
-
- CCObject(void);
-
- virtual ~CCObject(void);
-
- void release(void);
-
- void retain(void);
-
- CCObject* autorelease(void);
-
- CCObject* copy(void);
-
- bool isSingleRefrence(void);
-
- unsigned int retainCount(void);
-
- virtual bool isEqual(const CCObject* pObject);
-
- virtual void update(ccTime dt) {CC_UNUSED_PARAM(dt);};
-
- friend class CCAutoreleasePool;
- };
-
-
-
- typedef void (CCObject::*SEL_SCHEDULE)(ccTime);
-
- typedef void (CCObject::*SEL_CallFunc)();
-
- typedef void (CCObject::*SEL_CallFuncN)(CCNode*);
-
- typedef void (CCObject::*SEL_CallFuncND)(CCNode*, void*);
- typedef void (CCObject::*SEL_CallFuncO)(CCObject*);
-
- typedef void (CCObject::*SEL_MenuHandler)(CCObject*);
-
- typedef void (CCObject::*SEL_EventHandler)(CCEvent*);
-
- #define schedule_selector(_SELECTOR) (SEL_SCHEDULE)(&_SELECTOR)
- #define callfunc_selector(_SELECTOR) (SEL_CallFunc)(&_SELECTOR)
- #define callfuncN_selector(_SELECTOR) (SEL_CallFuncN)(&_SELECTOR)
- #define callfuncND_selector(_SELECTOR) (SEL_CallFuncND)(&_SELECTOR)
- #define callfuncO_selector(_SELECTOR) (SEL_CallFuncO)(&_SELECTOR)
- #define menu_selector(_SELECTOR) (SEL_MenuHandler)(&_SELECTOR)
- #define event_selector(_SELECTOR) (SEL_EventHandler)(&_SELECTOR)
- #define compare_selector(_SELECTOR) (SEL_Compare)(&_SELECTOR)
-
- NC_CC_END
- #endif // __CCOBJECT_H_
CCObject.cpp:
- #include "CCObject.h"
-
- #include "CCAutoreleasePool.h"
-
- #include "ccMacros.h"
-
- #include "scripte_support/CCScriptSupport.h"
-
- NS_CC_BEGIN
-
- CCObject* CCCopying::copyWithZone(CCZone *pZone)
- {
- CC_UNUSED_PARAM(pZone);
-
- CCAssert(0, "not implement");
- return 0;
- }
-
- CCObject::CCObject(void)
- {
-
- static unsigned int uObjectCount = 0;
-
-
- m_uID = ++uObjectCount;
-
- m_nLuaID = 0;
-
-
- m_uReference = 1;
-
- m_bManaged = false;
- }
-
- CCObject::~CCObject(void)
- {
- 如果内存是由内存管理器统一管理,则调用内存管理器实例对象的移除函数对自已的内存进行释放。
- if (m_bManaged)
- {
- CCPoolManager::getInstance()->removeObject(this);
- }
-
-
- if (m_nLuaID)
- {
- CCScriptEngineManager::sharedManager()->getScriptEngine()->removeCCObjectByID(m_nLuaID);
- }
- }
-
- CCObject* CCObject::copy()
- {
- return copyWithZone(0);
- }
-
- 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;
- }
-
- CCObject* CCObject::autorelease(void)
- {
-
- CCPoolManager::getInstance()->addObject(this);
-
- m_bManaged = true;
- return this;
- }
-
- bool CCObject::isSingleRefrence(void)
- {
-
- return m_uReference == 1;
- }
-
- unsigned int CCObject::retainCount(void)
- {
- return m_uReference;
- }
-
- bool CCObject::isEqual(const CCObject *pObject)
- {
- return this == pObject;
- }
-
- NS_CC_END
原理说明:
我们看到CCObject其实真的很单纯,它主要就是有两个功能。一个是通过引用计数交给内存管理器进行内存管理。另一个就是通过脚本ID访问相应的脚本。脚本的分析后面专门有一章进行分析探讨,我们暂时只把内存管理的事情搞明白。好,下面我们来重点看一下autorelease函数的意义,顾名思义,“自动释放”。也就是说调用此函数则当前CCObject实例对象不需要用户在外部去手动调用release进行内存的释放工作。我们已经知道它通过引用计数来处理在什么时候内存释放。Cocos2d-x是怎么做到的呢?
在autorelease函数中有这么一句
CCPoolManager::getInstance()->addObject(this);
CCPoolManager代表了内存管理器。此句调用CCPoolManager的实例对象的addObject函数将当前CCObject实例对象的指针交给内存管理器。我们来分析一下内存管理器的原理。
读前小提示:CCMutableArray是一个CCObject指针容器类,它内部通过使用STL的vector容器来存储CCObject指针。在加入一个新CCObject时对其引用计数器加1,在移除CCObject时对其引用计数器减1。请各位同学自行打开CCMutableArray.h及cpp文件进行查看。
重点函数:CCAutoreleasePool::release(),CCPoolManager::finalize()
CCAutoreleasePool.h:
- #ifndef __AUTORELEASEPOOL_H__
- #define __AUTORELEASEPOOL_H__
-
- #include "CCObject.h"
-
- #include "CCArray.h"
-
- NS_CC_BEGIN
-
- class CC_DLL CCAutoreleasePool : public CCObject
- {
-
- CCArray* m_pManagedObjectArray;
- public:
-
- CCAutoreleasePool(void);
-
- ~CCAutoreleasePool(void);
-
- void addObject(CCObject *pObject);
-
- void removeObject(CCObject *pObject);
-
- void clear();
- };
-
- 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;
- };
-
- NS_CC_END
-
- #endif //__AUTORELEASEPOOL_H__
CCAutoreleasePool.cpp:
虽然这个cpp讲解完了,但是可能有的朋友仍然会感觉身在云雾中一样。我再总结一下:Cocos2d-x提供了一个内存管理器类CCPoolManager,它有一个容器m_pReleasePoolStack,而这个容器是用来存放了一些容器管理类CCAutoreleasePool的实例对象的。需要自动进行内存释放的CCObject实例对象会把其指针存放在容器管理类CCAutoreleasePool的实例对象中的m_pManagedObjectArray容器里。所有存在其中的CCObject实例对象在进行释放操作时通过使用计数器来进行判断在何时真正delete内存。
现在我们来实际操作一下。我们在VC的代码查询里输入“CCPoolManager:: sharedPoolManager ()”然后回车。在查找结果里会看到:
查找全部"CCPoolManager::sharedPoolManager()", 大小写匹配, 子文件夹, 查找结果1,"整个解决方案"
C:\cocos2d-2.0-x-2.0.2\cocos2dx\cocoa\CCAutoreleasePool.cpp(90):CCPoolManager*CCPoolManager::sharedPoolManager()
C:\cocos2d-2.0-x-2.0.2\cocos2dx\cocoa\CCObject.cpp(58): CCPoolManager::sharedPoolManager()->removeObject(this);
C:\cocos2d-2.0-x-2.0.2\cocos2dx\cocoa\CCObject.cpp(93): CCPoolManager::sharedPoolManager()->addObject(this);
C:\cocos2d-2.0-x-2.0.2\cocos2dx\CCDirector.cpp(158): CCPoolManager::sharedPoolManager()->push();
C:\cocos2d-2.0-x-2.0.2\cocos2dx\CCDirector.cpp(181): CCPoolManager::sharedPoolManager()->pop();
C:\cocos2d-2.0-x-2.0.2\cocos2dx\CCDirector.cpp(971): CCPoolManager::sharedPoolManager()->pop();
第一个结果是函数定义,略过。
第二个结果是CCObject的析构,这里的意义就是如果CCObject的实例对象调用过autorelease将其内存管理交给内存管理器,则在析构时会调用内存管理器对其进行释放。
第三个结果是CCObject的autorelease函数。这里的意义是将CCObject的实例对象指针通过参数传给内存管理器,交由内存管理器对其内存进行管理。
第四个结果是在CCDirector的init函数中。这里的意义是在显示设备初始化时调用内存管理器的push函数新创建一个内存管理结点用来对后面的CCObject进行内存管理。
第五个结果是在CCDirector的析构函数中。这里的意义是在最终结束游戏时调用内存管理器的pop函数对其管理的当前内存管理结点进行清空。
第六个结果是在显示设备的每一帧渲染处理时调用内存管理器的pop函数对其管理的当前内存管理结点进行清空,看到了吧,每一帧进行调用,所以说即使它每一次只能释放容器中第nCount-1个CCAutoreleasePool实例对象。但它因为每帧都调用,所以始终可以保持其内部只有1个CCAutoreleasePool实例对象。在这里解答了伏笔3.1.1和伏笔3.1.2。当然,如果你在伏笔3.1.1处将
m_pReleasePoolStack->removeObjectAtIndex(0);
改为:
m_pReleasePoolStack->removeAllObjects();
更好理解一些。
谢天谢地,这一节终于基本上结束了。相信如果你坚持看完这一节的代码分析之后,你就会对Cocos2d-x的CCObject有一个清楚的认识,因为未来有很多CCObject的派生类需要我们去认识。所以这一节会对以后的理解大有帮助。好了,下一节课再见!
|