bug:把ui做成单件,析构的时候会在FontFreeType里访问野指针并闪退

版本:cocos2d-x 3.3final

貌似发错版了,请斑竹转到2dx版吧

把字体文件丢到resources目录下
把两个代码文件覆盖一下(重新上传了代码,但是字体太大无法上传,请自己搞一个字体吧,代码里用的xxx.ttf要对应字体文件修改一下)

直接跑起来之后关闭程序,就会闪退
错误是FontFreeType析构的时候,_fontRef会野指针

大体上是把widget做成单件,然后在appdelegate析构的时候,删除它
widget里面如果包含了ui::text并且用了字体(如果不用字体就没事),就会悲剧

无法上传附件怎么回事呢
testcode.zip (3 KB)

测试程序,随便建个demo,把代码搞到helloworld里
然后找个字体文件丢到Resources里,

加一个调用函数,在启动的时候调用test()
void HelloWorld::test()
{
TestWidget *widget = TestWidget::getInstance();
if(widget->getParent())
{
widget->removeFromParent();
}
this->addChild(widget);
widget->setPosition(Vec2(300, 300));
}

这是类定义
class TestWidget : public ui::Widget
{
public:
static TestWidget *getInstance();
static void destroy();

virtual bool init();

CREATE_FUNC(TestWidget);

static TestWidget *m_hInstance;

};

这是类实现
///////////////////////////////////
//TestWidget
TestWidget *TestWidget::m_hInstance = NULL;
TestWidget *TestWidget::getInstance()
{
if(!m_hInstance)
{
m_hInstance = TestWidget::create();
m_hInstance->retain();
}
return m_hInstance;
}
void TestWidget::destroy()
{
if(m_hInstance)
{
CC_SAFE_RELEASE_NULL(m_hInstance);
}
}
bool TestWidget::init()
{
if(!ui::Widget::init())
{
return false;
}

ui::Text *text = ui::Text::create("123", "HYShuHunJ.ttf", 30); ~!!!!!这里要修改一下字体文件
this->addChild(text);

return true;

}

AppDelegate析构里析构一下TestWidget:

AppDelegate::~AppDelegate()
{
TestWidget::destroy();
}

用cocos2d::Label也是一样的错误

bool TestWidget::init()
{
if(!ui::Widget::init())
{
return false;
}

cocos2d::Label *label = cocos2d::Label::create("123", "HYShuHunJ.ttf", 30);
this->addChild(label);

return true;

}

没人呢,自己顶!

找到原因了,是因为红色这句
DrawPrimitives::free();
会把字体都干掉,但是单件Widget里的Label没有得到通知,所以无法得知内存已经被删除,进行了二次释放
办法就是自己在想办法在purgeDirector的DrawPrimitives::free();之前通知一下自己,提前释放单件就可以了

void Director::purgeDirector()
{
// cleanup scheduler
getScheduler()->unscheduleAll();

// Disable event dispatching
if (_eventDispatcher)
{
    _eventDispatcher->setEnabled(false);
}

if (_runningScene)
{
    _runningScene->onExit();
    _runningScene->cleanup();
    _runningScene->release();
}

_runningScene = nullptr;
_nextScene = nullptr;

// remove all objects, but don't release it.
// runWithScene might be executed after 'end'.
_scenesStack.clear();

stopAnimation();

CC_SAFE_RELEASE_NULL(_FPSLabel);
CC_SAFE_RELEASE_NULL(_drawnBatchesLabel);
CC_SAFE_RELEASE_NULL(_drawnVerticesLabel);

// purge bitmap cache
FontFNT::purgeCachedData();

FontFreeType::shutdownFreeType();

// purge all managed caches

#if defined(GNUC) && ((GNUC >= 4) || ((GNUC == 3) && (GNUC_MINOR >= 1)))
#pragma GCC diagnostic ignored “-Wdeprecated-declarations”
#elif _MSC_VER >= 1400 //vs 2005 or higher
#pragma warning (push)
#pragma warning (disable: 4996)
#endif
DrawPrimitives::free();

应该算是比较好的修改方案:
加红色那段代码,因为字体库整体析构之后,会标记_FTInitialized为false,所以只要判断它为true,才能释放,否则就已经被字体库整体干掉了

FontFreeType::~FontFreeType()
{
if (_stroker)
{
FT_Stroker_Done(_stroker);
}
if (_fontRef && FontFreeType::_FTInitialized)
{
FT_Done_Face(_fontRef);
}

s_cacheFontData.referenceCount -= 1;
if (s_cacheFontData.referenceCount == 0)
{
    s_cacheFontData.erase(_fontName);
}

}

:12:传授了你一个掉渣天的技能居然一句鸣谢也木有。。。T T 伤心:4:

— Begin quote from ____

引用第5楼东扬冬阳于2015-01-21 21:47发表的 回 4楼(flysec) 的帖子 :
:12:传授了你一个掉渣天的技能居然一句鸣谢也木有。。。T T 伤心:4: http://www.cocoachina.com/bbs/job.php?action=topost&tid=282057&pid=1226689

— End quote

多谢版主!版主威武!
搞定了一个大bug啊!