当监听者注册时,什么能使本地作用域对象保持活动状态?

时间:2011-07-05 06:41:15

标签: flex actionscript-3

我在各种AS3代码中看到了这种明显的魔力,但这是一个简化的例子:

package {
    import flash.display.Sprite;
    import flash.events.*;
    import flash.net.*;

    public class URLLoaderExample extends Sprite {
        public function URLLoaderExample() {
            var loader:URLLoader = new URLLoader();
            loader.addEventListener(Event.COMPLETE, onComplete);
            loader.load(new URLRequest("example.txt");
        } // 'loader' should fall out of scope here!

        private function onComplete(evt:Event):void {
            var loader:URLLoader = URLLoader(evt.target);
            trace ("Received data: " + loader.data);
            //unsure if removal below is necessary (since I don't
            //know where 'loader' itself is hiding!)...
                //  - NOTE: this removal is never in the examples!
            loader.removeEventListener(Event.COMPLETE, onComplete);
        }
    }
}

如代码注释中所示,loader变量应在URLLoaderExample构造函数之后超出范围。然而......由于onComplete监听器/处理程序能够干净地接收它,它似乎仍然保持活着(不是垃圾收集)。

loader的魔术/隐藏/全局引用在哪里使其保持活动状态,以便它可以完成加载操作,然后传递给onComplete侦听器/回调?可以在某处看到这个参考吗?

为了帮助上下文......作为一个类似的例子,我知道loader实例将注册onComplete个侦听器。我也知道我需要小心谨慎地使用removeEventListener(?)以避免因搁浅的监听器而导致的潜在内存泄漏。令我担心的是,我不明白魔法loader引用的位置以及是否(或何时)需要清除它。

这可能是loader.load()调用本身在全球某处填充loader吗?

4 个答案:

答案 0 :(得分:5)

此示例肯定容易出错,因为加载程序可能在加载完成之前进行垃圾收集。当您使用COMPLETE方法订阅onComplete事件时,您可以从加载器创建引用到您的类URLLoaderExample。你需要确保GC不会破坏加载器正在创建一个的引用。

GC永远不会保证您及时清理,即使您明确杀死所有引用。 (See this post for resources on GC logics.)但是如果没有明确的引用,可以在进程中垃圾收集加载器。如果您在使用内存的应用程序中尝试测试(并且不只是坐在那里无所事事),您可能会看到此行为。如果您尝试加载swfs而不是数据,则更有可能看到加载器被垃圾收集。

使用弱引用在这里没有用,因为当你这样做时,你告诉GC:“随意杀死我,调度员,我所引用的,我对它没有任何遗憾。”在你的例子中,它将是:“如果它失去其他可行的引用,可以随意杀死URLLoaderExample实例”,这是毫无意义的。 Here's one good article on useWeakReference.

监听器不会阻止调度程序被垃圾收集。非活动对象是不再从其他活动对象引用它的对象。因此,如果一个对象本身具有对外部内容的引用,则不会阻止该对象从内存中删除。

所以,简要回答一下你的问题:参考文献无处可去,你很幸运能看到加载工作正常。好吧,完全准确,它是函数激活对象(在ECMA规范中称为),它用作局部变量的范围并引用它们。但无论如何,它会在方法返回时被处理掉,你永远无法获得对激活对象本身的引用(再次按规范)。

编辑关于谁拥有垃圾收集者的更多话语。由于评论中存在明显的误解而添加。

报价from Adobe livedocs

  

useWeakReference:Boolean(default = false) - 确定对侦听器的引用是强还是弱。强引用(默认值)可防止您的侦听器被垃圾回收。弱参考不会。

因此,订阅事件会创建一个引用FROM调度程序TO侦听器。与听众不同,调度员可以自由行动。监听器不会阻止调度程序被垃圾收集。调度员可以防止收听者被垃圾收集,这就是为什么我们有useWeakReference

答案 1 :(得分:1)

添加事件侦听器时,隐式创建对loader对象的引用(默认情况下)。但是,您可以通过将eventlistener设置为“弱”引用来删除它。

这是你如何做到的:

loader.addEventListener(Event.COMPLETE, onComplete, false, 0, true);

最后一个参数将“useWeakListener”设置为true,这意味着不会对加载器进行引用。在这种情况下,加载程序应该是GC。

要记住的重要一点是,如果添加具有强引用的事件侦听器,则需要将其删除(如示例中所示)。如果你使用弱侦听器,你需要在类中使加载器成为私有变量,否则你的回调与GC竞争。

以下是该方法的文档:http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/events/IEventDispatcher.html

答案 2 :(得分:1)

好的,如果你的问题是为什么装载机活在范围之外,这是你的答案:

实际上,您在类构造函数方法(CCM)中看到的加载器与您在onComplete中看到的不同:LINE14:var loader:URLLoader = URLLoader(evt.target);

(我不知道为什么人们喜欢将相同的名称添加到变量中,这可能令人困惑,但现在我不会解释这一点)

神奇之处在于evt.target。但你应该问自己:“.target做了什么?”它是由“事件对象”定义的实例变量,它提供对“目标对象”的引用。

如果您不知道“事件目标”是什么,请阅读本段。 “事件目标”是侦听器在LINE9中注册的对象:loader.addEventListener(Event.COMPLETE, onComplete);正如您所看到的,它是CCM中的加载器(请不要混淆加载器名称),这些加载器引用new URLLoader();。段落结论:“事件目标”是CCM上的load local变量引用的URLLoader对象。

好吧,使用.target变量,您可以引用URLLoader对象,然后根据需要使用引用。在这种情况下,您使用引用删除侦听器。没关系。

这是一个改进版本(只是一个实例变量,它为URLLoader提供了一个参考):

package {
    import flash.display.Sprite;
    import flash.events.*;
    import flash.net.*;

    public class URLLoaderExample extends Sprite {
        private var lalala:URLLoader;
        public function URLLoaderExample() {
            lalala = new URLLoader();
            lalala.addEventListener(Event.COMPLETE, onComplete);
            lalala.load(new URLRequest("example.txt");
        }

        private function onComplete(evt:Event):void {
            trace ("Received data: " + lalala.data);
            lalala.removeEventListener(Event.COMPLETE, onComplete);
        }
    }
}

但是为了让你感到舒服,你不要与名字混淆:

package {
    import flash.display.Sprite;
    import flash.events.*;
    import flash.net.*;

    public class URLLoaderExample extends Sprite {
        public function URLLoaderExample() {
            var blabla:URLLoader = new URLLoader();
            blabla.addEventListener(Event.COMPLETE, onComplete);
            blabla.load(new URLRequest("example.txt");
        } // 'loader' fell out of scope here! and it fell look there

        private function onComplete(evt:Event):void {
            var phopho:URLLoader = URLLoader(evt.target);
            trace ("Received data: " + phopho.data);
            loader.removeEventListener(Event.COMPLETE, onComplete);
        }
    }
}

...干杯 如果你对URLLoader(evt.target);使用的施法操作有任何疑问,你可以问。

答案 3 :(得分:0)

我不明白你的困惑。加载器变量 超出范围(例如,不再需要在内存中指向的引用)。但是,因为它仍然是对外部事件监听器的引用,所以加载器本身不是GC,这就是为什么你可以继续获取加载器的引用(在处理程序中,它'获得目标)并且在移除事件监听器之前不符合GC条件。

编辑: 对不起,我应该更清楚这个问题。我应该多喝咖啡。当我的意思是“外部”时,我的意思是加载器,因为它们依赖于浏览器操作。现在,由于我从未见过Flash Player的底层代码,所以我无法这样说,但我怀疑如果你有任何需要与浏览器交互的Loader类型类,那么一个下划线参考(从播放器到加载器),它将阻止它被GC编辑。这就是为什么你应该always unload your Loaders左右,这是我对Flash Player的理解。我可能是错的,但我过去做了几次测试,而且我从未见过外部听众(在这种情况下是装载机,但我想知道是否有&# <39; s可能与Air一起发生的任何其他外部侦听器)在没有明确的情况下获取GC。