2011-03-25 48 views
3

所有,AS3,垃圾收集和多层次的精灵与听众

我如果创建一个精灵,而精灵包含许多子Sprite(其本身也包含子精灵),做我需要担心garbage-收集一切,或只是父母精灵?如果孩子或孙辈的小精灵有听众呢?

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

该屏幕包含许多用于屏幕上UI可用性的Sprites(例如,按钮,下拉菜单,文本框等)。许多精灵都在其上有事件监听器。

所以,在我的申请,我有这样的:

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

后来,当用户与屏幕做,我将其删除:

this.removeChild(myscreen); 
myscreen = null; 

是所有我需要做的?或者,我是否需要通过myscreen递归去除所有它的子Sprites和Event Listeners?

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

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

提前感谢!

+1

你必须删除所有孩子的所有听众。即使父级设置为空,侦听器仍然存在。 – jpea 2011-03-25 18:07:18

回答

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将剪辑引用到root,将clip2引用到root。发生垃圾回收时,即使该侦听器在那里,标记也不能从根节点跳转到剪辑。参考是其他方向,从剪辑到根!所以对象仍然会被垃圾收集。因此,在这种情况下,不必删除监听器。也就是说,如果你不确定,这并不会伤害你。

的唯一途径听众可以防止垃圾回收是,如果一个孩子夹在听父剪辑:

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

此侦听器创建一个从根参考夹,所以您必须删除引用或使用弱引用。既然你使用的是弱引用,你也不必担心。

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

但是在使用简单MouseEvent的小对象图的情况下,即使您不打扰删除侦听器,您也可以。他们不会伤害任何东西,当剪辑从显示列表中删除时,他们将不再被分派。你可以只用removeChild父剪辑并完成它。

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