1

我正在创建一个小行星游戏,在我的主要课程中,我在处理飞船发射的子弹时遇到了一些麻烦。

所有子弹都属于“子弹”类,并存储在主类中名为“子弹”的数组中。当子弹退出屏幕时,主类中的 removeBullet(bulletID) 被调用。

private function removeBullet(id:int)
    {
        removeChild(bullets[id]);
        bullets.splice(id);
    }

在我的 Bullet 类中,我有一个跟踪“stillHere”的 enterFrame 侦听器。因此,一旦使用 addChild 将项目符号添加到主阶段,“stillHere”就会开始在我的输出面板中弹出。

我的问题是,即使我调用了 removeBullet,“stillHere”仍然在输出面板中弹出,这告诉我我试图删除的对象仍然停留在内存中的某个地方。

我该怎么做才能完全摆脱它?

4

2 回答 2

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 会导致性能下降,因此通过该措施尽你所能防止甚至需要它是一件好事。

于 2011-02-09T14:59:15.417 回答
4

因为您使用的是 ActionScript,所以您无法直接控制对象何时被移除。

您遇到的真正问题是您的事件侦听器仍在触发。您显然可以通过removeEventListener在删除它们时调用来解决这个问题。

然而,更好的方法是只ENTER_FRAME为整个游戏设置一个监听器。它需要单独推进所有游戏元素(船、小行星、子弹、碎片等)。此方法消除了您意外忘记删除事件侦听器的任何机会,并且还使代码更清晰,因为您可以看到元素在时间步内更新的顺序。

我通常destroy在我的临时对象中有一个函数,它的内容如下:

public function destroy():void {
    stop(); // if it's a MovieClip
    if(parent) parent.removeChild(this);
}

只要我调用这个函数然后删除对对象的引用,它通常会被收集。

由于这个原因,我的代码很少有单个对象的监听器。

于 2011-02-09T15:20:59.437 回答