在cocos2dx 3.3中下面myScene在切出时会存在概率性崩溃(代码作了最大程度简化,仅为说明问题):
class CmyLayer:public Layer{
public:
CmyLayer(){
m_sprite=NULL;
m_renderTex=NULL;
}
virtual~CmyLayer(){
}
bool init(){
m_sprite=Sprite::create("a.png");
this->addChild(m_sprite);
m_renderTex=RenderTexture::create(w,h);
this->addChild(m_renderTex);
return true;
}
void udpate(float dt){
m_renderTex->begin();
m_sprite->visit();
m_renderTex->end();
}
private:
RenderTexture* m_renderTex;
Sprite* m_sprite;
};
class CmyScene:public Scene{
public:
CmyScene(){
}
virtual~CmyScene(){
}
bool init(){
CmyLayer*myLayer=new CmyLayer();
myLayer->autorelease();
myLayer->init();
addChild(myLayer);
return true;
}
};
在myScene场景切出时概率性地崩溃在两个位置:
(1)RenderTexture的onBegin函数里,在RenderTexture的实现中onBegin是作为customCommand发出去的。
(2)崩溃在RenderTexture的GroupCommand里。
其中第一个崩溃位置出现频率占大多数。
崩溃在command里是最坑爹的,因为其执行是异步的,所以当崩溃时很难调查对象在发出此command时的当时状态,甚至连发出command的对象是谁都很难知道!数据/状态一致性问题和debug困难是我讨厌command机制的主要原因。
先停止吐槽,猜测崩溃原因,认为很可能是在场景切出时由于m_renderTex随myLayer一起销毁,但m_renderTex发出的customCommand已加入到command队列且尚未执行,这样当等到此customCommand执行时,它会调用m_renderTex的onBegin函数,但m_renderTex已销毁,所以产生不可预料的结果。
为了避免“对象已销毁但其customCommand已发出却尚未执行”而导致的“调用对象不存在”的情况出现,我尝试采取了下面两个措施:
1,在场景切出时尽早停止CmyLayer::update。(因为崩溃的customCommand是在CmyLayer::update的m_renderTex->begin()中发出的)
2,在场景切出时推迟销毁m_renderTex。
具体地,CmyLayer代码改成:
class CmyLayer:public Layer{
public:
CmyLayer(){
m_sprite=NULL;
m_renderTex=NULL;
}
virtual~CmyLayer(){
if(m_renderTex)m_renderTex->autorelease();//add, inorder to achieve delay release, use autorelease instead of release
}
bool init(){
m_sprite=Sprite::create("a.png");
this->addChild(m_sprite);
m_renderTex=RenderTexture::create(w,h);
this->addChild(m_renderTex);
m_renderTex->retain();//add
return true;
}
void udpate(float dt){
m_renderTex->begin();
m_sprite->visit();
m_renderTex->end();
}
void onExit(){
CCLayer::onExit();
this->unscheduleUpdate();//add, stop update as soon as possible
}
private:
RenderTexture* m_renderTex;
Sprite* m_sprite;
};
我不知道这两处改动是否都是必须的,由于是概率性崩溃,验证和重现比较耗时,所以在没有更清晰的分析之前暂时两处改动都保留。如此改之后目前为止还没有再发生崩溃。但这种修补显然是不自然的,不知道有没有更好的办法。
补充:
本例中由于m_sprite是普通Sprite,不包含customCommand,所以m_sprite不会存在“调用对象不存在”的问题,但假如m_sprite是一个自定义sprite或者是一个更复杂的子树结构且其中也含有customCommand的话,则m_sprite也需要像m_renderTex一样处理,使其推迟释放。