cocos2d 3.11.1使用粒子效果引起的EXC_BAD_ACCESS问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/iamlegendary/article/details/77532131

最近准备构建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 [-1] to [6]

通过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[0]));
        __indexCapacity = indicesCount;
    }

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

    _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[0]));

        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 = 0; i < __indexCapacity / 6; i++)
    {
        __indices[i * 6 + 0] = (GLushort)(i * 4 + 0);
        __indices[i * 6 + 1] = (GLushort)(i * 4 + 1);
        __indices[i * 6 + 2] = (GLushort)(i * 4 + 2);
        __indices[i * 6 + 3] = (GLushort)(i * 4 + 3);
        __indices[i * 6 + 4] = (GLushort)(i * 4 + 2);
        __indices[i * 6 + 5] = (GLushort)(i * 4 + 1);
    }

    _indexSize = indicesCount;
}

后面在看3.15的时候,发现官方解决了这个问题,且方案应该更为妥当。
这里写图片描述
将原来的_indices放入_ownedIndices中,这样保证原有的frame映射的存储器区域不会放生改变,在析构函数中释放_ownedIndices。这样做也避免了过多的反复resizing即malloc.

如若不想替换引擎可以手动替换掉3.15下 cocos/render/CCQuadCommand.cpp和
对应.h文件,即可解决crash的问题。

猜你喜欢

转载自blog.csdn.net/iamlegendary/article/details/77532131