前两天忙于做老师布置的新任务——制作一款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中也是一样。
看书时见过好多次警告竟然还犯这样的错误,写此文章好让自己长长记性。
分享到:
相关推荐
Java 实例 - for 和 foreach循环使用源代码-详细教程.zip
前端开源库-gulp-foreach吞下foreach,将每个文件按流发送到自己的流中
java中foreach循环的使用方法!
改进了linux tailq队列下在遍历时删除多个元素的问题。可以使用TAILQ_FOREACH_SAFE下面进行节点删除操作。不会出现野指针问题
C-FOREACH用法需要遍历的集合放到items中,然后定义一个temp为每次应用到的集合元素,然后设置起步值及叠加步数
前端el表达式应用。判断、循环
Objective-C中的类型推断 该项目包括几个宏和类类别,这些宏和类类别提供了有关编译器类型推断的提示。 从受Swift启发的简单let和var到替换for (in) {}循环的foreach () {}构造。 如果没有类型推断,则类型容易出错...
利用foreach循环显示数组所有元素,对foreach循环进行更深了解
大厂前端面试|# 开始 ...## 注意事项 - 技术深度,就有那么 1-2 个方面即可。深了,就不可能全面 - 技术深度的题目不过关,也不一定就面试不通过 ## 看几个面试题 列举几个代表性的题目,参考视频课程
循环动态判断checkbox是否被选中foreach(Control c in Panel1.Controls)
具体来说,它定义了一个包含三个水果名称的数组$fruits,然后使用foreach循环遍历这个数组,并将每个元素赋值给变量$fruit。在每次循环中,我们使用echo语句输出当前元素的值,并在其后面添加一个换行符" "。最后...
具体来说,它定义了一个包含三个水果名称的数组$fruits,然后使用foreach循环遍历这个数组,并将每个元素赋值给变量$fruit。在每次循环中,我们使用echo语句输出当前元素的值,并在其后面添加一个换行符" "。最后...
cforEach标签的使用祥解。c:forEach用法的详细介绍。
为循环控制,它可以将集合(Collection)中的成员循序浏览一遍。运作方式为当条件符合时,就会持续重复执行<c:forEach>的本体内容。 语法 语法1:迭代一集合对象之所有成员 <c:forEach [var="varName"] items=...
Delphi Foreach循环的用法实例,演示如何使用For Each生成循环,用赋值于文本框控件中,程序试图使用多种方式生成Foreach,并最终清除它。本示例面向Delphi基础学者,了解Delphi基础知识的一些应用。
java-array-list-foreach
<php+mysql>PHP脚本条件判断,foreach循环,以及粘性表单
循环语句为众多程序员们提供了很大的便利,有while、do…while、for和 foreach。而且foreach语句很简洁,但是它的优点不仅仅在于此,它的效率也是最高的。本文将详细给大家关于C#中foreach循环对比for循环的优势和...
ForEach 循环控制器是比较重要的逻辑控制器,但是一般不会单独使用需跟用户自定义的变量组件一起使用,ForEach 循环控制器从用户定义的变量中读取一系列相关的变量。该控制器下的采样器或控制器都会被执行一次或多...
计算机前端-核心编程. Smarty08foreach循环数组.avi