由一个内存错误发现的cocos2dx 引擎3.4版本的 一个bug

在cocos2dx 3.4版本中,场景B到C,再到B,再回C,最后从C返回B时,程序崩溃。通过调试发现,startScene引用计数异常,由menuItem的onExit()函数触发。原因是引擎在menuItem中错误地维护了target指针,导致额外释放。此问题已在新版cocos2dx中解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

错误症状

最近做实训cocos2dx项目的TA,一个同学在场景切换时遇到如下问题,场景B->C,C->B,B->C,然后场景C->B,返回场景B时程序就崩溃了

主要涉及代码
bool StartScene::init() {
      if (!Scene::init())
        return  false;
    MenuItemFont *skipMenuItem =           MenuItemFont::create("skip",this, menu_selector(StartScene::SkipMenuItemCallback));
    skipMenuItem->setAnchorPoint(Vec2(0, 0));
    skipMenuItem->setPosition(Vec2(900, 25));
    Menu *skipMenu = Menu::create(skipMenuItem, NULL);
    skipMenu->setAnchorPoint(Vec2::ANCHOR_BOTTOM_LEFT);
    skipMenu->setPosition(0, 0);
    this->addChild(skipMenu);
    return true;

回调函数

void StartScene::SkipMenuItemCallback(Ref* sender)
 {
auto levelChosenScene = LevelChooseScene::createScene();
Director::getInstance()->pushScene(levelChosenScene);
}
bool LevelChooseScene::init() {
    if (!Scene::init())
        return  false;
    CCMenuItemFont *levelChosenMenuItem = CCMenuItemFont::create("LevelChosen", this, menu_selector(LevelChooseScene::LevelChosenMenuItemCallback));
    levelChosenMenuItem->setAnchorPoint(Vec2(0, 0));
    levelChosenMenuItem->setPosition(Vec2(500, 300));
    CCMenu *levelChosenMenu = CCMenu::create(levelChosenMenuItem, NULL);
    levelChosenMenu->setAnchorPoint(Vec2::ANCHOR_BOTTOM_LEFT);
    levelChosenMenu->setPosition(0, 0);
    this->addChild(levelChosenMenu);

    return true;
}

点击回调函数:

void LevelChooseScene::LevelChosenMenuItemCallback(Ref* sender) {
    Director::getInstance()->popScene();
}

错误定位

通过简单调试,发现startScene的引用计数异常,在一创建之后就为2,而在第二次pop之后变为0,而另外一个TA发现把startScene中的menuitem用CC_CALLBACK_1创建而不是menu_selector创建的话就没问题

MenuItemFont *skipMenuItem = MenuItemFont::create("skip", CC_CALLBACK_1(StartScene::SkipMenuItemCallback,this));

发现由原来的函数创建时:

bool MenuItemFont::initWithString(const std::string& value, Ref* target, SEL_MenuHandler selector)
{
    CCASSERT( !value.empty(), "Value length must be greater than 0");

    _target = target;
    CC_SAFE_RETAIN(target);
    return initWithString(value, std::bind(selector,target, std::placeholders::_1) );
}

会维护了target指针,此例中就是StartScene指针
然后就是定位_target何时release(),

因为scene继承Ref,在Ref的release()函数中增加语句

    if (!strcmp(typeid(*this).name(), "class StartScene"))
    {
        CCLOG("hehe");
    }

然后把断点设在CCLOG处,观察堆栈帧
这里写图片描述
发现在menuitem::onExit()函数中
这里写图片描述
会realse()一次,导致了错误的发生
因为每次切换场景都对scene额外多realse()了一次,引擎的bug在于不应该在menuitem中维护target指针

这个问题已经在最新版的cocos2dx引擎中解决了,新版本中不在提供menu_item 维护target指针的create函数重载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值