再看CCObject,剔除上节的拷贝相关,以及Lua脚本相关的属性和方法后,CCObject还剩下什么?
剩下什么?
可以看到整个CCObject就是围绕着m_uReference和m_uAutoReleaseCount在转。这两个变量的解释如下。所以CCObject剩下的其实就是对内存的管理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| CCObject::CCObject(void) : m_nLuaID(0) , m_uReference(1) , m_uAutoReleaseCount(0) { static unsigned int uObjectCount = 0; m_uID = ++uObjectCount; } CCObject::~CCObject(void) { if (m_uAutoReleaseCount > 0) { CCPoolManager::sharedPoolManager()->removeObject(this); } if (m_nLuaID) { CCScriptEngineManager::sharedManager()->getScriptEngine()->removeScriptObjectByCCObject(this); } else { CCScriptEngineProtocol* pEngine = CCScriptEngineManager::sharedManager()->getScriptEngine(); if (pEngine != NULL && pEngine->getScriptType() == kScriptTypeJavascript) { pEngine->removeScriptObjectByCCObject(this); } } } 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::sharedPoolManager()->addObject(this); return this; } bool CCObject::isSingleReference(void) const { return m_uReference == 1; } unsigned int CCObject::retainCount(void) const { return m_uReference; } bool CCObject::isEqual(const CCObject *pObject) { return this == pObject; } void CCObject::acceptVisitor(CCDataVisitor &visitor) { visitor.visitObject(this); }
|
内存管理
从CCObject可以看出,内存的管理方式有两种:手动管理和自动管理。
手动内存管理
想必从Java转到C++的朋友可能很受不了C++再申请完内存后还要手动释放,就像我从C++转Java时也同样很不习惯竟然不用管理内存,老是害怕会不小心让系统给销毁了。CCObject的成员变量m_uAutoReleaseCount标识了是手动管理还是自动管理。如果执行以下操作:
1
| CCObject* obj=new CCObject();
|
从析构函数可以看到,析构函数是不对所有手动进行申请的变量进行内存释放(必须m_uAutoReleaseCount>0)。那么这时得手动释放:
所以,new和release是好基友!而手动内存管理一般不再使用retain。
自动内存管理
如果需要进行内存的自动管理,那要怎么做呢?
1 2
| CCObject* obj=new CCObject(); obj->autorelease();
|
好了,什么都不用做,obj自生自灭了。
如果我们需要随时用到obj,而不愿意让它在我们不知情的情况下被释放,那么使用:
当不再需要它的时候,使用:
又恢复回去了,所以,retain和release是好基友,一般在自动内存管理使用。
这里只是大概写一下如何使用new,autorelease,retain和release,至于内存管理的实现网上的代码解析很多,发现自己没办法深入浅出地写出来,所以还是放弃再写一回注释了,原理可以这么理解:Cocos2d-x提供了一个内存管理器类CCPoolManager,它包含了一个CCArray容器m_pReleasePoolStack,这个容器用来存放一些容器管理类CCAutoreleasePool的实例对象。需要自动进行内存释放的CCObject实例对象会把其指针存放在容器管理类CCAutoreleasePool的实例对象中的m_pManagedObjectArray容器里。所有存在其中的CCObject实例对象在进行释放操作时通过使用计数器来进行判断在何时真正释放内存,游戏在每一帧结束时都会对autorelease对象进行释放。
一个疑问
平时我们可能会这么用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| class CTestLayer : public cocos2d::CCLayer { public: virtual bool init(); CREATE_FUNC(CTestLayer); virtual void update(float delta); private: CCSprite* background; };
bool CTestLayer::init() { if ( !CCLayer::init() ) { return false; } background=CCSprite::create("background.png"); this->addChild(background); this->scheduleUpdate(); return true; } void update(float delta) { background->setPositionY(background->getPositionY()-0.1); }
|
background是create出来的,可以知道它是调用了autorelease,属于自动管理对象,而且我们又没有进行retain操作,按道理在执行update的时候background已经是要被销毁的,但是实际并没有。问题就出在这一句:
1
| this->addChild(background);
|
至于为什么,大家翻一下addChild实现源码就知道啦~