评论

收藏

【深入Cocos2d-x】探索Cocos2d-x中的内存管理-引用计数和自动释放池

游戏开发 游戏开发 发布于:2021-07-17 21:36 | 阅读数:419 | 评论:0

#深入Cocos2d-x-探索Cocos2d-x中的内存管理-引用计数和自动释放池
###引用计数(Reference Count)
引用计数是一种在C++中相当古老的内存管理方法,ios中将这种机制包括在NSAutoreleasePool中。所有我们在Cocos2d-x中也有个相似的东西,叫CCAutoreleasePool,用处基本上一样,详细请看:NSAutoreleasePool Class Reference
###CCAutoRealeasePool
CCAutoRealeasePool与NSAutoreleasePool有着相同的概念和架构,但有两个重要区别:
      
  • CCAutoreleasePool不能嵌套,所以每个cocoss2d-x的游戏实例中仅能有一个,开发者不能创建新的CCAutoreleasePool,而只是注意CCOject的释放和增加引用计数(realease/retain)就可以了  
  • CCAutoreleasePool不能在多线程中使用 。所以如果你的游戏需要一个网络线程,请仅仅接收数据或者改变网络中的状态值,不要调用其中的Cocos2d接口。
CCAutoreleasePool的逻辑是这样的:当你调用object->autorealease()这个方法时,这个object就被放在了CCAutoreleasePool中,这个计数池能帮你拿着这个object,想一个管家一样的保存到当前message loop的结尾。等到了当前message loop的结尾如果这个object没有被任何一个class或者container持有(retain)的话,它就被自动释放出来。例如:layer->addChild(sprite),这个sprite被加到layer的children list中去,那么它的生命周期就一直保持到这个layer被释放的时候,而不是当前message loop的结尾就被释放。
所以啦,你就不能在网络线程中管理CCObject啦:在每个UI线程结束时,autorealease object就会被删除掉,当你调用这些已经被删除掉的对象的指针的话,game就会崩掉
###CCObject::release(), retain() and autorelease()
总而言之,有两种情况你需要调用 ->realease()方法
      
  • 当你通过new的方式创建一个CCObject时  
  • 当你得到一个CCObject的指针,然后在代码里调用“retain”方法
下面是一个例子,他不需要realease()
CCSprite* sprite = CCSprite::create("player.png");
如果你看了源码的话,你会发现在create方法里CCSprite已经调用了autorealease
###使用静态构造方法
CCSprite::create("player.png") 就是用了静态构造方法. 在Cocos2d-x中的所有类, 除了单件, 都提供了这种create方法,在其中包含了四个操作:
      
  • new an object  
  • call object->init(...)  
  • if init success, e.g. find the texture file successfully, it will call object->autorelease();  
  • return the object marked as autorelease.
所有的 CCAsdf::createWithXxxx(...) 类型的函数都™一个行为
在使用create方法时,你不用考虑new,delete,autorealease之类的事,只要专注于这一对:object->retain() 和 object->release()
###一个错误的实例
一个开发者弄CCArray时弄崩了
<!-- lang: cpp -->
bool HelloWorld::init()
{
  bool bRet = false;
  do
  {
    //////////////////////////////////////////////////////////////////////////
    // super init first
    //////////////////////////////////////////////////////////////////////////
    CC_BREAK_IF(! CCLayer::init());
    //////////////////////////////////////////////////////////////////////////
    // add your codes below...
    //////////////////////////////////////////////////////////////////////////
    CCSprite* bomb1 = CCSprite::create("CloseNormal.png");
    CCSprite* bomb2 = CCSprite::create("CloseNormal.png");
    CCSprite* bomb3 = CCSprite::create("CloseNormal.png");
    CCSprite* bomb4 = CCSprite::create("CloseNormal.png");
    CCSprite* bomb5 = CCSprite::create("CloseNormal.png");
    CCSprite* bomb6 = CCSprite::create("CloseNormal.png");
    addChild(bomb1,1);
    addChild(bomb2,1);
    addChild(bomb3,1);
    addChild(bomb4,1);
    addChild(bomb5,1);
    addChild(bomb6,1);
    m_pBombsDisplayed = CCArray::create(bomb1,bomb2,bomb3,bomb4,bomb5,bomb6,NULL);
    //m_pBombsDisplayed is defined in the header as a protected var.
    // <--- We should add m_pBombsDisplayed->retain() here to avoid crashing in HelloWorld::refreshData()
    this->scheduleUpdate();
    bRet = true;
  } while (0);
  return bRet;
}
void HelloWorld::update(ccTime dt)
{
  refreshData();
}
void HelloWorld::refreshData()
{
  m_pBombsDisplayed->objectAtIndex(0)->setPosition(cpp(100,100));
}
这哥们犯了什么错误呢?m_pBombsDisplayed是由create方法创建的,他被标记为autorealease,
所以在message loop结束时CCArray就被删除了
当随后的message loop调用 HelloWorld::update(ccTime)时,m_pBombsDisplayed已经是null了,所以解决办法是在create方法后加上retain,在析构方法中加realease
翻译原文请点击这里

关注下面的标签,发现更多相似文章