`
modabobo
  • 浏览: 510097 次
文章分类
社区版块
存档分类
最新评论

CCARRAY_FOREACH应用误区

 
阅读更多

背景:

CCArray是cocos2d-x提供的非常好用一个容器类,为了方便对遍历容器里面的元素,2dx提供了CCARRAY_FOREACH这样的宏。

问题引入:

现在在做的一个塔防项目,恶魔打天使,每一个天使都存放在一个CCArray对象m_pMonsterArray中。在一个schedue中判断,当天使死掉后,就从m_pMonsterArray中删除。代码想当然的写成如下:

code_segment1

一开是没仔细注意看,然后看结果好像也是对的。后来为了定位另外一个问题,加了一些log信息,才发现这段代码隐藏的bug。

问题定位:

为了方便调试,将所有monster的HP都设置为0,预期只要执行一次上面的代码,m_pMonsterArray就应该被清空。但是实际上第一轮没执行完,就已经报了”std::__non_rtti_object at memory location 0x003EF430″的错误,打印出来的log如下:

Add monster, uid=78
Add monster, uid=79
Add monster, uid=80
Add monster, uid=81
Add monster, uid=82
Add monster, uid=83
Add monster, uid=84
Add monster, uid=85
Add monster, uid=86
Add monster, uid=87

******************
The 1 times calling update
Clear monster, uid=78
Clear monster, uid=80
Clear monster, uid=82
Clear monster, uid=84
Clear monster, uid=86
Clear monster, uid=87

从log中可以看出,删除的时候是隔一个删一个,与我们便利array中所有元素的初衷相差甚远,所以,一定是什么地方用错了。

首先仔细看看CCARRAY_FOREACH这个宏,可以在cocoa/CCArray.h中找到它的定义

CCARRAY_FOREACH

这里采用了指针移动的方式来提高效率。看到这个,大概心里有个数了,因为在array中删除了一个元素,如果这个元素后面的所有元素指针都“向前移动一次”,那么就会导致这种情况。为了确认这个想法,继续看CCArray的removeObject方法,一路追下去,其最终调用了support/data_support/ccArray.cpp中的ccArrayRemoveObjectAtIndex函数。

CCARRAY_MOVE

确实,在移除需要移除的元素后,其后的所有元素都“向前移动一次”。

因此,在CCARRAY_FOREACH中进行removeObject是一种非常不安全的操作,可能导致漏删,或者导致数组越界(用CCARRAY_FOREACH为下溢,用CCARRAY_FOREACH_REVERSE为上溢)。

解决方案:

我用了一个比较笨的解决方案,先在CCARRAY_FOREACH中将所有需要删除的元素放在一个临时CCAarry对象中,然后再遍历该临时CCArray,从m_pMonsterArray中删除,代码如下:

修改后截图

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics