AS3,垃圾收集和带有监听器的多级Sprite

时间:2011-03-25 17:57:22

标签: flash actionscript-3 garbage-collection

所有

我是否创建了一个sprite,并且该sprite包含许多子sprite(它们本身也包含子sprite),我是否需要担心垃圾收集所有内容,或者只是父级sprite?如果孩子或孙子精灵有听众怎么办?

例如,假设我有一个类(Sprite的子类)在应用程序中创建一个屏幕。

该屏幕包含许多用于屏幕上所有UI功能的精灵(例如,按钮,下拉菜单,文本字段等)。许多精灵都有事件监听器。

所以,在我的申请中,我有这个:

var myscreen:MyScreenClass = new MyScreenClass();
this.addChild(myscreen);

稍后,当用户完成该屏幕后,我将其删除:

this.removeChild(myscreen);
myscreen = null;

这就是我需要做的吗?或者,我是否需要递归通过myscreen,删除所有它的子Sprite和事件监听器?

换句话说,如果你是父母的GC,那么所有的孩子,孙子孙女,曾孙子等也是GC的吗?

(对于它的价值,我在我的事件监听器中使用弱引用......)

提前致谢!

2 个答案:

答案 0 :(得分:10)

如果删除对父对象的所有引用,则不必删除子对象或子对象的侦听器,假设没有对子对象的外部引用。

标记清除垃圾收集器通过遍历对象图来工作,从最顶层的对象(即,舞台)开始。剪掉图形的一部分的所有路径,并且整个子图将有资格收集,无论子图本身有任何引用。

首先,让我们考虑一下没有事件的显示列表层次结构:

var clip:Sprite = new Sprite();
addChild(clip);
var clip2 = new Sprite();
clip.addChild(clip2);
// cleanup
removeChild(clip);
clip = null;
clip2 = null;

内部子clip2将被垃圾收集,即使我们没有通过clip.removeChild(clip2)将其从父项中删除。由于我们删除了对父clip和显式clip2引用的所有引用,因此无法访问它,因此将对其进行垃圾回收。因此,没有必要removeChild后代剪辑。只需确保清除对它们的任何外部引用(在本例中为clip2)。

现在让我们想象一些事件:

var clip:Sprite = new Sprite();
addChild(clip);
clip.addEventListener(MouseEvent.CLICK, someListener);
var clip2:Sprite = new Sprite();
clip.addChild(clip2);
clip2.addEventListener(MouseEvent.CLICK, someOtherListener);
// cleanup -- the same!
removeChild(clip);
clip = null;
clip2 = null;

您可能认为必须删除事件侦听器,但实际上并不是必需的。 addEventListener创建从调度程序到侦听器的引用。也就是说,向子对象添加侦听器不会阻止它们的垃圾回收。在这种情况下,addEventListener从剪辑到根,并将clip2引用到root。当垃圾收集发生时,标记不能从根跳到剪辑,即使该监听器在那里。从剪辑到根,参考是其他方向!所以对象仍将被垃圾收集。因此,在这种情况下没有必要删除侦听器。也就是说,如果你不确定,这样做并没有什么坏处。

侦听器可以阻止垃圾回收的唯一方法是子剪辑正在侦听父剪辑:

// from inside clip
root.addEventListener(MouseEvent.CLICK, someHandler);

此侦听器创建从root到clip的引用,因此您必须删除该引用或使用弱引用。由于您使用的是弱引用,因此您也不必担心这一点。

确实有很多需要跟踪的内容,并且很容易出错,所以最好在完成后删除侦听器。如果你删除它们,你将永远是安全的。 Flash为您调度的奇怪的本机事件非常重要,例如Event.ENTER_FRAME和KeyboardEvent.KEY_DOWN,但不是因为垃圾收集问题:即使剪辑没有引用且有资格收集,它也会继续收到ENTER_FRAME事件,直到垃圾收集器实际上在未来的某个不确定点运行。所以你应该总是删除ENTER_FRAME监听器。

但是对于带有简单MouseEvents的小对象图,即使您不打算删除侦听器,也可以。它们不会伤害任何东西,当剪辑从显示列表中删除时,它们将不再被分派。您只需removeChild父剪辑即可完成。

如果您想查看正在发生的事情,使用Flash Builder,FlashDevelop或FDT中的探查器工具来查看内存使用情况会很有帮助。如果要测试这些想法,也可以使用System.gc();调用强制GC在调试模式下运行。

答案 1 :(得分:4)