您的位置:首页 > 移动开发 > Cocos引擎

Cocos2d-x 2.0.4 小心隐藏的retain

2013-07-29 14:13 190 查看
Cocos2d-x中的CCObject类及其派生类,使用autorelease()方法,将自身交托于CCPoolManager管理器进行管理,都可以使用retain()方法来使自身的引用计数加一,使用release()方法来使自身的引用计数减一,当引用计数为0的时候,CCPoolManager管理器就会将其删除释放。

所有实例化Cocos2d-x里面的以CCObject为基类的类时,都要使用其create()方法来创建对象,对于自己添加的派生类,需要通过CREATE_FUNC宏来实现create()方法,下面以《如何制作一个横版格斗过关游戏
Cocos2d-x 2.0.4》来举例介绍:
Hero.h

1

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宏定义如下:

1

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; \

        return NULL; \

    } \

}
具体可以类似如下:
SimpleDPad.h

1

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()方法,具体实现如下:

1

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;

        return NULL;

    }

}
当然这里的方法名可以改为以create开头方便统一。
变量
当create出来的变量,被addChild到以CCNode为基类的类时,或者被addObject到CCArray、CCSet等时,都会自动将这个变量对象retain()一次,以防止被自动释放导致的野指针问题,所以一般情况都不需要再手动调用retain()方法了。对于类定义中用CC_SYNTHESIZE_RETAIN宏声明的变量,或者对临时变量手动调用了retain()方法,一般都需要在析构函数或者特定的函数进行手动调用release()方法,类似如下:
GameLayer.h

1

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

1

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

1

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

1

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指针传递下去,跟踪源码:

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

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);

    return NULL;

}

bool CCCallFunc::initWithTarget(CCObject* pSelectorTarget) {

    if (pSelectorTarget) 

    {

        pSelectorTarget->retain();

    }

    if (m_pSelectorTarget) 

    {

        m_pSelectorTarget->release();

    }

    m_pSelectorTarget = pSelectorTarget;

    return true;

}
可以看到它对this指针的对象进行retain()调用,导致Hero对象无法被自动释放,需要先手动对_attackAction变量进行release()调用,CCCallFunc将进行析构,进而将会对this指针的对象进行release()调用

1

2

3

4

virtual ~CCCallFunc()

{

    CC_SAFE_RELEASE(m_pSelectorTarget);

}
解决方法举例如下:
ActionSprite.h

1

2

3

4

5

6

class ActionSprite : public cocos2d::CCSprite

{
public:

    //……
   virtual void cleanup();

};  
ActionSprite.cpp

1

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检测,均未检测到内存泄露。

如文章存在错误之处,欢迎指出,以便改正。By 无幻
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: