CCCallFuncN误用导致引用计数循环引用

时间:2023-03-10 07:00:42
CCCallFuncN误用导致引用计数循环引用

昨天测试“角色被遮挡部分透明显示”功能时,发现角色死亡后,其轮廓精灵不会消失。调试发现,角色在死亡时,其引用计数retain_count居然是9。这是由引用计数混乱引起的内存泄露。

加了很多日志跟踪retain_count,又通过调试,终于确定了问题,是我错误使用CCCallFuncN这个CCAction导致的。于是查看cocos2d-x2.2.1源代码了解这个类的实现原理。

CCCallFuncN是CCAction的子类,是函数回调动作。我在游戏中用这个类来实现“角色死亡后倒地4秒渐渐消失再删除精灵”功能。

PathFinder::_deadAction = CCSequence::createWithTwoActions(
CCFadeOut::create(4.0f),
CCCallFuncN::create(this, callfuncN_selector(PathFinder::callbackDead)));

其中,CCCallFuncN::create的第一个参数this将作为方法PathFinder::callbackDead的参数,CCCallFuncN内部会对其retain一次。

所以,PathFinder的成员引用了PathFinder,其retain_count至少是1,因此永远不会析构,循环引用导致内存泄露。

刚发现这个错误,我愚蠢地在_deadAction创建后面加了一个this->release(),这样日志中retain_count正常了,可是忘了当PathFinder析构时析构_deadAction又会对PathFinder做release()操作,导致retain_count变为负数断言。

改成这样即可,注意runAction时也会retain调用对象,目前还没发现问题:

_deadAction = CCSequence::createWithTwoActions(
CCFadeOut::create(4.0f),
CCRemoveSelf::create());

对于必须要使用CCFunc的函数,建议把该函数放到其他类中。

经验:

1.千万不能让对象持有引用了该对象的成员,特别是精灵create这种隐蔽的情况;

2.精灵create时,可能会retain传入的其他对象,当然我一般不会retain,因为有自己的内存管理规则。

3.析构函数加日志,确保确实析构。