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

mat*_*ler 3 flash garbage-collection actionscript-3

所有,

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

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

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

所以,在我的应用程序中,我有这个:

var myscreen:MyScreenClass = new MyScreenClass();
this.addChild(myscreen);
Run Code Online (Sandbox Code Playgroud)

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

this.removeChild(myscreen);
myscreen = null;
Run Code Online (Sandbox Code Playgroud)

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

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

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

提前致谢!

Mik*_*lsh 10

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

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

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

var clip:Sprite = new Sprite();
addChild(clip);
var clip2 = new Sprite();
clip.addChild(clip2);
// cleanup
removeChild(clip);
clip = null;
clip2 = null;
Run Code Online (Sandbox Code Playgroud)

内部子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;
Run Code Online (Sandbox Code Playgroud)

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

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

// from inside clip
root.addEventListener(MouseEvent.CLICK, someHandler);
Run Code Online (Sandbox Code Playgroud)

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

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

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

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