`
lever0066
  • 浏览: 54755 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

使用Objective-C中foreach循环的一大注意事项

 
阅读更多
前两天忙于做老师布置的新任务——制作一款iPhone上的“雷电”,做的还算成功,玩起来颇有乐趣(可能由于是自己的劳动成果吧)。

    在制作该款小游戏时我的任务之一是对子弹等需要大量产生并销毁的对象进行管理。之所以要管理这些对象是由于:如果只是简单的在发射子弹时分配内存并初始化子弹对象,在子弹消失时(飞出屏幕或达到对方)销毁此对象,开销会非常大——因为在Objective-C中对象的内存都是动态分配的(用malloc),但动态分配内存有可能非常慢(因为分配时有很多复杂的情况,比如说你请求内存时操作系统有可能先把把一些内存碎片整合起来再交给你)。对于来得快去的也快的子弹对象,我们应该采用的处理办法是建立一个“子弹对象池”,当子弹应该被销毁时并不把它的内存释放,而是把它放到“子弹对象池”中,以后需要子弹时我们就从此对象池中取出一个对象重新初始化即可,不必为它重新分配内存了。

    构建管理这些子弹对象的池需要用到能动态增长的数组,NSMutableArray是首选,而且Objective-C中有方便的foreach循环,我也就毫不客气的使用了。结果就有了如下代码片断:



    // 遍历正在活动的子弹对象池

    for(Bullet* bullet in activeBulletPool)

    {

        // 如果子弹撞到了敌人,应该被移出活动子弹对象池,放入不活动子弹对象池(炸毁)

       if([bullet collidesWithSomething])

       {

           [activeBulletPool removeObject:bullet];

           [deactiveBulletPool addObject:bullet];

        }

    }



    结果运行时会报如下错误:Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <NSCFArray: 0x3906590> was mutated while being enumerated.'。

    从字面上看意思是在遍历一个集合时该集合被改变了,感觉甚是不解,于是请来粟帮忙一起调试,还是他比较敏锐,认为在foreach循环里改变正在被遍历的数组似乎不是很合适,于是改代码为:



    // 遍历正在活动的子弹对象池

    for(int i = 0; i < activeBulletPool; ++i)

    {

        Bullet* bullet = [activeBulletPool objectAtIndex:i];

        // 如果子弹撞到了敌人,应该被移出活动子弹对象池,放入不活动子弹对象池(炸毁)

        if([bullet collidesWithSomething])

       {

           [activeBulletPool removeObject:bullet];

           --i;

           [deactiveBulletPool addObject:bullet];

        }

    }



    果然就好了,在佩服粟的敏锐之时我也仔细分析了一下问题的本质原因:应该是Objective-C中的foreach循环与Java中的相似,在内部是用iterator(迭代器)实现遍历的。而不管是在Java还是C++中,一旦修改了被遍历对象,在修改前生成的iterator都会失效,所以《C++ Primer》及Java课本中曾警告过不要在用iterator遍历集合时增删集合元素,看来Objective-C中也是一样。

    看书时见过好多次警告竟然还犯这样的错误,写此文章好让自己长长记性。
分享到:
bak
评论

相关推荐

Global site tag (gtag.js) - Google Analytics