天天看點

cocos2d 3.11.1使用粒子效果引起的EXC_BAD_ACCESS問題

最近準備建構IOS的包,在運作時進入遊戲場景之前直接crash掉了。在CCEAGLView-ios.mm檔案中抛出了一個EXC_BAD_ACCESS的錯誤。

安卓和win32平台上運作都是沒問題的。

錯位定位的代碼位置是

#endif // __IPHONE_4_0

     if(![context_ presentRenderbuffer:GL_RENDERBUFFER])
        {
//         CCLOG(@"cocos2d: Failed to swap renderbuffer in %s\n", __FUNCTION__);
        }

#if COCOS2D_DEBUG
           

之前沒接觸個這種錯誤,是以查了一下這個EXC_BAD_ACCESS到底是何方神聖!

總結了一下,簡單來說就是:當遇到EXC_BAD_ACCESS這個錯誤,那就意味着你向一個已經釋放的對象發送消息。(大部分都是這樣,當然也有特例)

詳細的解釋:當您向一個對象發送消息時,指向該對象的指針将會被引用。這意味着你擷取了指針所指的記憶體位址,并通路該存儲區域的值。

當存儲器區域不再映射到你的應用時,即改記憶體區域在你認為使用的時候卻沒有使用,則改記憶體區域是無法通路的。這時核心就會抛出一個異常(EXC),表明你的應用程式不能通路該存儲區域(BAD ACCESS)。簡言之,當你碰到了EXC_BAD_ACCESS時,意味着你試圖發送消息到一個記憶體塊,但該記憶體塊無法發送該消息。但某些情況下,EXC_BAD_ACCESS是由某個被損壞的指針引起的,當引用損壞的指針時,也會抛出該異常。

cocos2d: QuadCommand: resizing index size from [-] to []
           

通過log日志可以看出跟QuadCommand有關系,且上面這條日志是加入了遊戲背景的粒子後引起的,是以我斷定了跟粒子效果有關系。

先看看QuadCommand相關的代碼:

void QuadCommand::reIndex(int indicesCount)
{
    if (indicesCount > __indexCapacity)
    {
        CCLOG("cocos2d: QuadCommand: resizing index size from [%d] to [%d]", __indexCapacity, indicesCount);
        __indices = (GLushort*) realloc(__indices, indicesCount * sizeof(__indices[]));
        __indexCapacity = indicesCount;
    }

    for( int i=; i < __indexCapacity/; i++)
    {
        __indices[i*6+] = (GLushort) (i*4+);
        __indices[i*6+] = (GLushort) (i*4+);
        __indices[i*6+] = (GLushort) (i*4+);
        __indices[i*6+] = (GLushort) (i*4+);
        __indices[i*6+] = (GLushort) (i*4+);
        __indices[i*6+] = (GLushort) (i*4+);
    }

    _indexSize = indicesCount;
}
           

這裡有一個問題,如果執行reIndex次數過多,那麼可能出現一種情況,針對realloc函數如果原先的記憶體大小後面沒有足夠的空閑空間用來配置設定,那麼從堆中另外找一塊newsize大小的記憶體。并把原來大小記憶體空間中的内容複制到newsize中。傳回新的mem_address指針。(資料被移動了)老塊被放回堆上。,按照上面對EXC_BAD_ACCESSD的解釋,可以想到_indices是正在被使用的,即Frame在Draw的過程中資料可能被移動了,那麼Frame之前映射到的存儲器區域被修改,則導緻在繪制想通路

該存儲器位置時,無法通路而抛出異常。

參考了http://discuss.cocos2d-x.org/t/exc-bad-access-random-crashes-on-ios-context-presentrenderbuffer/28494/9

解決方案,修改reIndex函數,在繪制玩Frame後,将_indices記憶體釋放掉:

void QuadCommand::reIndex(int indicesCount)
{
    if (indicesCount > __indexCapacity)
    {
        CCLOG("cocos2d: QuadCommand: resizing index size from [%d] to [%d]", __indexCapacity, indicesCount);
        auto tmpindices = __indices;
        __indices = (GLushort*)malloc(indicesCount * sizeof(__indices[]));

        void** listenerHolder = new void*();

        cocos2d::EventListenerCustom* listener = cocos2d::Director::getInstance()->getEventDispatcher()->addCustomEventListener(Director::EVENT_AFTER_DRAW, [=](cocos2d::EventCustom *event) {

            if (tmpindices)
            {
                free(tmpindices);
            }

            // unregister event listener
            cocos2d::Director::getInstance()->getEventDispatcher()->removeEventListener((EventListener*)*listenerHolder);

            if (listenerHolder)
            {
                delete listenerHolder;
            }

        });

        *listenerHolder = listener;

        __indexCapacity = indicesCount;
    }

    for (int i = ; i < __indexCapacity / ; i++)
    {
        __indices[i *  + ] = (GLushort)(i *  + );
        __indices[i *  + ] = (GLushort)(i *  + );
        __indices[i *  + ] = (GLushort)(i *  + );
        __indices[i *  + ] = (GLushort)(i *  + );
        __indices[i *  + ] = (GLushort)(i *  + );
        __indices[i *  + ] = (GLushort)(i *  + );
    }

    _indexSize = indicesCount;
}
           

後面在看3.15的時候,發現官方解決了這個問題,且方案應該更為妥當。

cocos2d 3.11.1使用粒子效果引起的EXC_BAD_ACCESS問題

将原來的_indices放入_ownedIndices中,這樣保證原有的frame映射的存儲器區域不會放生改變,在析構函數中釋放_ownedIndices。這樣做也避免了過多的反複resizing即malloc.

如若不想替換引擎可以手動替換掉3.15下 cocos/render/CCQuadCommand.cpp和

對應.h檔案,即可解決crash的問題。

繼續閱讀