在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

    if(m_sprite)m_sprite->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_sprite->retain();//add

    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;

};

我不知道这两处改动是否都是必须的,由于是概率性崩溃,验证和重现比较耗时,所以在没有更清晰的分析之前暂时两处改动都保留。如此改之后目前为止还没有再发生崩溃。但这种修补显然是不自然的,不知道有没有更好的办法。

05-06 20:03