Cocos2d-x 多线程场景导致游戏崩溃

问题描述

我的场景很简单:我使用 cocos2d-x 制作了一个游戏,我想为多人游戏用户下载图像(FB 和 Google play),并在下载完成后将它们显示为按钮的纹理。 在理想的世界中,事情按预期进行。 当这些按钮在下载完成之前被删除时,事情就会变得棘手。 所以回调函数处于奇怪的状态,然后我得到信号 11 (SIGSEGV),代码 1 (SEGV_MAPERR) 并且应用程序崩溃 这就是我实现它的方式 我有一个名为 PlayerIcon 的布局类。 cpp 看起来像这样

void PlayerIcon::setPlayer(string userName,string displayName,string avatarUrl){
    try {
        //some code here
        downloadAvatar(_userName,_avatarUrl);
        //some code here
    }
    catch(... ){
    }
}

void PlayerIcon::downloadAvatar(std::string _avatarFilePath,std::string url) {
    if(!isFileExist(_avatarFilePath)) {
        try {
            auto downloader = new Downloader();
            downloader->onFileTaskSuccess=CC_CALLBACK_1(PlayerIcon::on_download_success,this);
            downloader->onTaskError=[&](const network::DownloadTask& task,int errorCode,int errorCodeInternal,const std::string& errorStr){
                log("error while saving image");

            };
            downloader->createDownloadFileTask(url,_avatarFilePath,_avatarFilePath);
        }
        catch (exception e)
        {
            log("error while saving image: test");
        }
    } else {
        //set texture for button
    }
}

void PlayerIcon::on_download_success(const network::DownloadTask& task){
    _isDownloading = false;
    Director::getInstance()->getScheduler()-> performFunctionInCocosThread(CC_CALLBACK_0(PlayerIcon::reload_avatar,this));
}

void PlayerIcon::reload_avatar(){
    try {
        // setting texture in UI thread
    }
    catch (...) {
        log("error updating avatar");
    }
}

正如我所说,在下载完成之前删除 PlayerIcon 之前,一切正常。 我不知道当下载任务的回调指向已删除(或标记删除)的 un 对象方法时会发生什么。 我查看了下载器实现,它没有提供任何取消机制 我不知道如何处理这个

此外,cocos2dx 游戏在谷歌控制台上出现 10% 的崩溃率是否正常 任何帮助都非常感谢

解决方法

是否在 PlayerIcon 的析构函数中删除了 de Downloader?

苹果实现中有一个销毁,由析构函数触发。

-(void)doDestroy
{
// cancel all download task
NSEnumerator * enumeratorKey = [self.taskDict keyEnumerator];
for (NSURLSessionDownloadTask *task in enumeratorKey)
{
....


DownloaderApple::~DownloaderApple()
{
    DeclareDownloaderImplVar;
    [impl doDestroy];
    DLLOG("Destruct DownloaderApple %p",this);
}
,

在 cocos2d-x 的演示代码中:DownloaderTest.cpp 他们使用:

std::unique_ptr<network::Downloader> downloader;

downloader.reset(new cocos2d::network::Downloader());

代替:

auto downloader = new Downloader();
,

看起来您正在将此网络代码构建为场景树的一部分。如果你做一个 replaceScene/popScene...() 调用,而异步网络软件在后台运行,这将导致回调消失(场景将从场景中删除-堆栈),你会从中得到一个 SEGFAULT

如果这是您编码的方式,那么您可能希望将网络代码提取到一个全局对象(单例)中,在其中将请求排队,然后从 Internet 上抓取它们,将结果保存在全局对象的输出队列(或它们的名称和位置),然后让场景代码通过查询全局对象并在此时加载头像精灵来检查是否已收到头像。

请注意,这可能是间歇性问题,取决于您的机器和网络的速度,因此可能不会持续触发。

另一种解决方案......

或者您可以在 nullptr::PlayerIcon(析构函数)中将函数指针设置为 ~PlayerIcon()

downloader->setOnFileTaskSuccess(nullptr);
downloader->setOnTaskProgress(nullptr);

这样就不会尝试调用您的回调函数,SEGFAULT 将被避免(希望如此)。