最近準備建構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的時候,發現官方解決了這個問題,且方案應該更為妥當。

将原來的_indices放入_ownedIndices中,這樣保證原有的frame映射的存儲器區域不會放生改變,在析構函數中釋放_ownedIndices。這樣做也避免了過多的反複resizing即malloc.
如若不想替換引擎可以手動替換掉3.15下 cocos/render/CCQuadCommand.cpp和
對應.h檔案,即可解決crash的問題。