【玩转cocos2d-x之二十】从CCObject看cocos2d-x的内存管理机制
2015-07-21 15:24
260 查看
[b]原创作品,转载请标明:[/b]/article/1384955.html
再看CCObject,剔除上节的拷贝相关,以及Lua脚本相关的属性和方法后,CCObject还剩下什么?
[cpp] view
plaincopy
CCObject::CCObject(void)//构造函数
: m_nLuaID(0)
, m_uReference(1) //引用计数,初始为1,当引用计数=0,自动释放该对象
, m_uAutoReleaseCount(0)//自动释放对象计数,如果是手动new的,则为0,如果autoRelease的,则在AutoreleasePool会+1
//这里是保护成员,所以CCAutoreleasePool被声明为友元类
{
static unsigned int uObjectCount = 0;//静态成员,对象的计数,只增不减,用于标识唯一一个对象实例
m_uID = ++uObjectCount;
}
CCObject::~CCObject(void)
{
if (m_uAutoReleaseCount > 0)//如果是自动管理,则在PoolManager中删除
{
CCPoolManager::sharedPoolManager()->removeObject(this);
}
// if the object is referenced by Lua engine, remove it
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)//引用计数-1,如果引用计数=0,释放对象
{
CCAssert(m_uReference > 0, "reference count should greater than 0");
--m_uReference;
if (m_uReference == 0)
{
delete this;
}
}
void CCObject::retain(void)//引用计数+1,防止被对象释放
{
CCAssert(m_uReference > 0, "reference count should greater than 0");
++m_uReference;
}
CCObject* CCObject::autorelease(void)//对象加入PoolManager,自动管理
{
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);
}
[cpp] view
plaincopy
CCObject* obj=new CCObject();//m_uAutoReleaseCount=0,m_uReference=1
从析构函数可以看到,析构函数是不对所有手动进行申请的变量进行内存释放(必须m_uAutoReleaseCount>0)。那么这时得手动释放:
[cpp] view
plaincopy
obj->release();//m_uReference-1后为0,执行delete this;
所以,new和release是好基友!而手动内存管理一般不再使用retain。
[cpp] view
plaincopy
CCObject* obj=new CCObject();//m_uAutoReleaseCount=0,m_uReference=1
obj->autorelease();//m_uAutoReleaseCount=1,m_uReference=1
好了,什么都不用做,obj自生自灭了。
如果我们需要随时用到obj,而不愿意让它在我们不知情的情况下被释放,那么使用:
[cpp] view
plaincopy
obj->retain();//m_uAutoReleaseCount=1,m_uReference=2
当不再需要它的时候,使用:
[cpp] view
plaincopy
obj->release();//m_uAutoReleaseCount=1,m_uReference=1
又恢复回去了,所以,retain和release是好基友,一般在自动内存管理使用。
这里只是大概写一下如何使用new,autorelease,retain和release,至于内存管理的实现网上的代码解析很多,发现自己没办法深入浅出地写出来,所以还是放弃再写一回注释了,原理可以这么理解:Cocos2d-x提供了一个内存管理器类CCPoolManager,它包含了一个CCArray容器m_pReleasePoolStack,这个容器用来存放一些容器管理类CCAutoreleasePool的实例对象。需要自动进行内存释放的CCObject实例对象会把其指针存放在容器管理类CCAutoreleasePool的实例对象中的m_pManagedObjectArray容器里。所有存在其中的CCObject实例对象在进行释放操作时通过使用计数器来进行判断在何时真正释放内存,游戏在每一帧结束时都会对autorelease对象进行释放。
[cpp] view
plaincopy
//CTestLayer.h
class CTestLayer : public cocos2d::CCLayer
{
public:
virtual bool init();
CREATE_FUNC(CTestLayer);
virtual void update(float delta);
private:
CCSprite* background;
};
//CTestLayer.cpp
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已经是要被销毁的,但是实际并没有。问题就出在这一句:
[cpp] view
plaincopy
this->addChild(background);
至于为什么,大家翻一下addChild实现源码就知道啦~
再看CCObject,剔除上节的拷贝相关,以及Lua脚本相关的属性和方法后,CCObject还剩下什么?
1.剩下什么?
可以看到整个CCObject就是围绕着m_uReference和m_uAutoReleaseCount在转。这两个变量的解释如下。所以CCObject剩下的其实就是对内存的管理。[cpp] view
plaincopy
CCObject::CCObject(void)//构造函数
: m_nLuaID(0)
, m_uReference(1) //引用计数,初始为1,当引用计数=0,自动释放该对象
, m_uAutoReleaseCount(0)//自动释放对象计数,如果是手动new的,则为0,如果autoRelease的,则在AutoreleasePool会+1
//这里是保护成员,所以CCAutoreleasePool被声明为友元类
{
static unsigned int uObjectCount = 0;//静态成员,对象的计数,只增不减,用于标识唯一一个对象实例
m_uID = ++uObjectCount;
}
CCObject::~CCObject(void)
{
if (m_uAutoReleaseCount > 0)//如果是自动管理,则在PoolManager中删除
{
CCPoolManager::sharedPoolManager()->removeObject(this);
}
// if the object is referenced by Lua engine, remove it
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)//引用计数-1,如果引用计数=0,释放对象
{
CCAssert(m_uReference > 0, "reference count should greater than 0");
--m_uReference;
if (m_uReference == 0)
{
delete this;
}
}
void CCObject::retain(void)//引用计数+1,防止被对象释放
{
CCAssert(m_uReference > 0, "reference count should greater than 0");
++m_uReference;
}
CCObject* CCObject::autorelease(void)//对象加入PoolManager,自动管理
{
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);
}
2.内存管理
从CCObject可以看出,内存的管理方式有两种:手动管理和自动管理。2.1.手动内存管理
想必从Java转到C++的朋友可能很受不了C++再申请完内存后还要手动释放,就像我从C++转Java时也同样很不习惯竟然不用管理内存,老是害怕会不小心让系统给销毁了。CCObject的成员变量m_uAutoReleaseCount标识了是手动管理还是自动管理。如果执行以下操作:[cpp] view
plaincopy
CCObject* obj=new CCObject();//m_uAutoReleaseCount=0,m_uReference=1
从析构函数可以看到,析构函数是不对所有手动进行申请的变量进行内存释放(必须m_uAutoReleaseCount>0)。那么这时得手动释放:
[cpp] view
plaincopy
obj->release();//m_uReference-1后为0,执行delete this;
所以,new和release是好基友!而手动内存管理一般不再使用retain。
2.2.自动内存管理
如果需要进行内存的自动管理,那要怎么做呢?[cpp] view
plaincopy
CCObject* obj=new CCObject();//m_uAutoReleaseCount=0,m_uReference=1
obj->autorelease();//m_uAutoReleaseCount=1,m_uReference=1
好了,什么都不用做,obj自生自灭了。
如果我们需要随时用到obj,而不愿意让它在我们不知情的情况下被释放,那么使用:
[cpp] view
plaincopy
obj->retain();//m_uAutoReleaseCount=1,m_uReference=2
当不再需要它的时候,使用:
[cpp] view
plaincopy
obj->release();//m_uAutoReleaseCount=1,m_uReference=1
又恢复回去了,所以,retain和release是好基友,一般在自动内存管理使用。
这里只是大概写一下如何使用new,autorelease,retain和release,至于内存管理的实现网上的代码解析很多,发现自己没办法深入浅出地写出来,所以还是放弃再写一回注释了,原理可以这么理解:Cocos2d-x提供了一个内存管理器类CCPoolManager,它包含了一个CCArray容器m_pReleasePoolStack,这个容器用来存放一些容器管理类CCAutoreleasePool的实例对象。需要自动进行内存释放的CCObject实例对象会把其指针存放在容器管理类CCAutoreleasePool的实例对象中的m_pManagedObjectArray容器里。所有存在其中的CCObject实例对象在进行释放操作时通过使用计数器来进行判断在何时真正释放内存,游戏在每一帧结束时都会对autorelease对象进行释放。
2.3.一个疑问
平时我们可能会这么用:[cpp] view
plaincopy
//CTestLayer.h
class CTestLayer : public cocos2d::CCLayer
{
public:
virtual bool init();
CREATE_FUNC(CTestLayer);
virtual void update(float delta);
private:
CCSprite* background;
};
//CTestLayer.cpp
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已经是要被销毁的,但是实际并没有。问题就出在这一句:
[cpp] view
plaincopy
this->addChild(background);
至于为什么,大家翻一下addChild实现源码就知道啦~
相关文章推荐
- 【玩转cocos2d-x之十九】从CCObject看cocos2d-x的拷贝机制
- 【玩转cocos2d-x之十八】仿《中国好学霸》文字拖拽和定位
- 【玩转cocos2d-x之十七】cocos2d-x中文显示
- 【玩转cocos2d-x之十六】滚动字幕和公告
- 【玩转cocos2d-x之十五】关卡选择的设计
- 【玩转cocos2d-x之十四】菜单选项卡的实现
- 【玩转cocos2d-x之十三】CCMenu的布局
- 【玩转cocos2d-x之十二】plist解析工具:Anti_TexturePacker
- 【玩转cocos2d-x之十一】定时器schedule
- 【玩转cocos2d-x之十】cocos2d-x坐标系
- 【玩转cocos2d-x之九】动作类CCAction
- 【玩转cocos2d-x之八】精灵类CCSprite
- 【玩转cocos2d-x之七】场景类CCScene和布景类CCLayer
- 【玩转cocos2d-x之六】节点类CCNode
- 【玩转cocos2d-x之五】导演类CCDirector
- 【玩转cocos2d-x之四】cocos2d-x怎么实现跨平台
- 【玩转cocos2d-x之三】cocos2d-x游戏是怎么跑起来的
- 【玩转cocos2d-x之二】游戏和引擎构成
- 【总结】Cocos2d-x实用方法整理
- 【玩转cocos2d-x之一】V2.2.0版本开发环境配置