最近准备构建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的问题。