我正在创建一个小行星游戏,在我的主要课程中,我在处理船发射的子弹时遇到了一些麻烦。
所有子弹都属于“Bullet”类,并存储在主类中称为“项目符号”的数组中。当项目符号退出屏幕时,将调用主类中的removeBullet(bulletID)。
private function removeBullet(id:int)
{
removeChild(bullets[id]);
bullets.splice(id);
}
在我的Bullet类中,我有一个跟踪“stillHere”的enterFrame监听器。因此,只要使用addChild将子弹添加到主舞台,就会在我的输出面板中弹出“stillHere”。
我的问题是,即使在我调用removeBullet之后,“stillHere”仍然会在输出面板中弹出,这告诉我,我试图删除的对象仍然在内存中的某处。
我可以做些什么来完全摆脱它?
答案 0 :(得分:4)
事件监听器本身很可能是它仍然在内存中的原因。这不是测试某些东西是否被垃圾收集的好方法。
此外,即使这是检查对象是否已被垃圾收集的好方法,收集也不是一个瞬时过程。 Flash Player只在需要分配更多内存时才会运行gc,并且无法执行此操作。
假设除了显示列表和项目符号数组之外没有其他对项目符号的引用,你所做的就足以让它被垃圾收集了。
编辑:回答是否有办法观察是否收集了一个物品的问题......
您可以在弱键字典中将对象用作键。
private var _dict:Dictionary = new Dictionary(true);
_dict[bullet] = "Bullet is still here...";
然后,只要你想检查子弹是否仍然存在,你就可以使用for ... in循环来迭代键
for(var key:* in _dict){
trace(key + " " + _dict[key]);
}
因为弱键控字典的键不计入垃圾收集的引用,所以这是有效的。
如果您非常关心内存泄漏,可以考虑编写一个对象池,将放置在舞台上的旧子弹对象放入其中,然后一遍又一遍地重复使用它们。通过这种方式,你永远不会允许任何子弹被垃圾收集,但你可能只会创建一个有限数量的子弹(这是用户同时在屏幕上看到的子弹数量)在给定的时间)。这对你来说可能是最好的解决方案,因为子弹可能占用的内存很少,而且你可以获得不强制Flash清理垃圾的好处。在垃圾被移除时运行GC会导致性能下降,因此通过该措施尽可能地防止甚至需要它是一件好事。
答案 1 :(得分:4)
由于您使用的是ActionScript,因此无法直接控制何时删除对象。
您遇到的真正问题是您的事件侦听器仍在触发。显然,您可以在删除时调用removeEventListener
来解决此问题。
然而更好的方法是整个游戏只有一个ENTER_FRAME
监听器。它需要单独推进所有游戏元素(船舶,小行星,子弹,碎片等)。此方法消除了您不小心忘记删除事件侦听器的任何可能性,并且它还使代码更清晰,因为您可以看到元素将在一个时间步骤内更新的顺序。
我的临时对象中通常有destroy
函数,其内容如下:
public function destroy():void {
stop(); // if it's a MovieClip
if(parent) parent.removeChild(this);
}
只要我调用此函数然后删除对象的引用,通常会收集它。
出于这个原因,我的代码很少有个别对象的监听器。