Cocos2d-x 注意隐藏的retain 内存管理
Cocos2d-x中的CCObject类及其派生类,使用autorelease()方法,将自身交托于CCPoolManager管理器进行管理,都可以使用retain()方法来使自身的引用计数加一,使用release()方法来使自身的引用计数减一,当引用计数为0的时候,CCPoolManager管理器就会将其删除释放。
类
所有实例化Cocos2d-x里面的以CCObject为基类的类时,都要使用其create()方法来创建对象,对于自己添加的派生类,需要通过CREATE_FUNC宏来实现create()方法,下面以《如何制作一个横版格斗过关游戏 Cocos2d-x 2.0.4》来举例介绍:
Hero.h
2
3
4
5
6
7
8
9
class Hero : public ActionSprite
{
public:
Hero(void);
~Hero(void);
CREATE_FUNC(Hero);
//……
};若是需要create()方法带有参数的话,仿造CREATE_FUNC的定义来实现,CREATE_FUNC宏定义如下:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define CREATE_FUNC(__TYPE__) \
static __TYPE__* create() \
{ \
__TYPE__ *pRet = new __TYPE__(); \
if (pRet && pRet->init()) \
{ \
pRet->autorelease(); \
return pRet; \
} \
else \
{ \
delete pRet; \
pRet = NULL; \
returnNULL; \
} \
}具体可以类似如下:
SimpleDPad.h
2
3
4
5
6
7
8
9
10
class SimpleDPad : public cocos2d::CCSprite, public cocos2d::CCTargetedTouchDelegate
{
public:
SimpleDPad(void);
~SimpleDPad(void);
static SimpleDPad* dPadWithFile(cocos2d::CCString *fileName, float radius);
bool initWithFile(cocos2d::CCString *filename, float radius);
//……
};其中dPadWithFile静态方法就是仿造的create()方法,具体实现如下:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SimpleDPad* SimpleDPad::dPadWithFile(CCString *fileName, float radius)
{
SimpleDPad *pRet = new SimpleDPad();
if (pRet && pRet->initWithFile(fileName, radius))
{
pRet->autorelease();
return pRet;
}
else
{
delete pRet;
pRet = NULL;
returnNULL;
}
}当然这里的方法名可以改为以create开头方便统一。
变量
当create出来的变量,被addChild到以CCNode为基类的类时,或者被addObject到CCArray、CCSet等时,都会自动将这个变量对象retain()一次,以防止被自动释放导致的野指针问题,所以一般情况都不需要再手动调用retain()方法了。对于类定义中用CC_SYNTHESIZE_RETAIN宏声明的变量,或者对临时变量手动调用了retain()方法,一般都需要在析构函数或者特定的函数进行手动调用release()方法,类似如下:
GameLayer.h
2
3
4
5
6
7
8
9
10
class GameLayer : public cocos2d::CCLayer, public SimpleDPadDelegate
{
public:
GameLayer(void);
~GameLayer(void);
CREATE_FUNC(GameLayer);
//……
CC_SYNTHESIZE_RETAIN(cocos2d::CCArray*, _robots, Robots);
};GameLayer.cpp
2
3
4
5
6
7
8
9
10
11
GameLayer::GameLayer(void)
{
//……
_robots = NULL;
}
GameLayer::~GameLayer(void)
{
//……
CC_SAFE_RELEASE_NULL(_robots);
}但是有一种特殊情况,类与变量的互相retain(),导致无法释放,内存泄露。
ActionSprite.h
2
3
4
5
6
7
8
9
class ActionSprite : public cocos2d::CCSprite
{
public:
ActionSprite(void);
~ActionSprite(void);
//……
CC_SYNTHESIZE_RETAIN(cocos2d::CCAction*, _attackAction, AttackAction);
};Hero.cpp
2
3
4
5
6
7
8
9
10
11
12
13
bool Hero::init()
{
bool bRet = false;
do
{
//……
this->setAttackAction(CCSequence::create(CCAnimate::create(attackAnimation), CCCallFunc::create(this, callfunc_selector(Hero::idle)), NULL));
//……
} while (0);
return bRet;
} _attackAction变量以CCSequence类创建,CCSequence创建包含CCCallFunc的创建,CCCallFunc创建的时候将this指针传递下去,跟踪源码:
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
CCCallFunc * CCCallFunc::create(CCObject* pSelectorTarget, SEL_CallFunc selector)
{
CCCallFunc *pRet = new CCCallFunc();
if (pRet && pRet->initWithTarget(pSelectorTarget)) {
pRet->m_pCallFunc = selector;
pRet->autorelease();
return pRet;
}
CC_SAFE_DELETE(pRet);
returnNULL;
}
bool CCCallFunc::initWithTarget(CCObject* pSelectorTarget) {
if (pSelectorTarget)
{
pSelectorTarget->retain();
}
if (m_pSelectorTarget)
{
m_pSelectorTarget->release();
}
m_pSelectorTarget = pSelectorTarget;
returntrue;
}可以看到它对this指针的对象进行retain()调用,导致Hero对象无法被自动释放,需要先手动对_attackAction变量进行release()调用,CCCallFunc将进行析构,进而将会对this指针的对象进行release()调用
2
3
4
virtual ~CCCallFunc()
{
CC_SAFE_RELEASE(m_pSelectorTarget);
}解决方法举例如下:
ActionSprite.h
2
3
4
5
6
class ActionSprite : public cocos2d::CCSprite
{
public:
//……
virtual void cleanup();
}; ActionSprite.cpp
2
3
4
5
6
7
8
void ActionSprite::cleanup()
{
CC_SAFE_RELEASE_NULL(_idleAction);
CC_SAFE_RELEASE_NULL(_attackAction);
CC_SAFE_RELEASE_NULL(_walkAction);
CC_SAFE_RELEASE_NULL(_hurtAction);
CC_SAFE_RELEASE_NULL(_knockedOutAction);
CCSprite::cleanup();
} 最后用Visual Leak Detector和DevPartner检测,均未检测到内存泄露。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。