匿名事件监听器的更好选择是什么?

时间:2010-07-20 12:07:17

标签: actionscript-3 events garbage-collection

我正在尝试找出使用匿名事件侦听器的最佳选择,这些侦听器在使用后会100%收集垃圾。我得到了这两个例子,但我想知道它们之间是否真的有任何区别......

var listener1:Function = function(e:Event):void
{
 resource.removeEventListener(e.type, listener1);
 loadedHandler(resource);
 listener1 = null;
};
resource.addEventListener(ResourceEvent.LOADED, listener1);

或者这个......

resource.addEventListener(ResourceEvent.LOADED, function(e:Event):void
{
 Resource(e.currentTarget).removeEventListener( e.type, arguments["callee"]);
 loadedHandler(resource);

}, false, 0, true);

或者还有其他更好的解决方案吗?对我来说非常重要的是,这些侦听器和函数可以从内存中正确删除,因为它们可能在应用程序中经常执行。我可以去使用一个字典来映射所有的监听器等,然后在非匿名监听器等等中测试和删除它们,但是这会很快得到很快的参与,因为可能存在资源可以同时异步加载的情况在应用程序的不同类中。


@dominic: 你的意思是将这样的函数放入方法体中,对吧?正如我在上面写的那样,应用程序异步加载资源,并且可能发生当前正在加载资源,而来自应用程序中其他位置的另一个类请求相同的资源。资源管理类(其中所述代码所在的)然后连接到由资源调度的事件侦听器。据我所知,如果我使用类示例或函数作为侦听器,它们将被新请求重用,旧请求的事件将永远不会触发。因此匿名函数存储在变量中。我假设他们都留在记忆中,直到他们各自的要求完成。 但也许我完全混淆了这种情况并非如此?我有时发现事件很难掌握。

5 个答案:

答案 0 :(得分:2)

第一个看起来更干净,您可以避免使用强制转换和数组查找来删除侦听器。 你为什么使用“匿名”处理函数? 你可以像这样使用它,它有点干净,这就是asdocs建议使用事件模型的方式。

function listener1(e:ResourceEvent):void
{
    resource.removeEventListener(e.type, listener1);
    loadedHandler(resource);
};
resource.addEventListener(ResourceEvent.LOADED, listener1);

答案 1 :(得分:2)

这不直接回答你的“vs”问题,但我即将发布一个名为Reactive Extensions的.NET raix框架的AS3端口,它解决了这样的问题:

Observable.fromEvent(resource, ResourceEvent.LOADED)
    .take(1)
    .subscribe(function(e : ResourceEvent) : void
    {
        // just the subscriber code
    });

take的使用意味着它会在第一个事件后自动取消订阅。如果您不喜欢匿名函数,也可以手动订阅对方法的引用,而不必担心以后删除它。

虽然不打算这样做,但如果这个答案过于“垃圾”,请告诉我,我会将其删除。

答案 2 :(得分:1)

事件处理函数/方法在这里确实不是问题。

如果我理解你的意思,那就是你有一个ResourceManager类,当你的应用中的一个类请求时,它会加载某个资源。资源完全加载后,资源将调度将触发事件处理程序的事件:

resource.addEventListener(ResourceEvent.LOADED, listener1);

现在将调用listener1函数或方法。 问题是,如果在listener1中你决定删除事件监听器,现在进一步加载资源的完成将触发listener1处理程序,因为管理器不再监听它。 如果应用程序中的两个不同的类同时加载资源,则每个ResourceEvent.LOADED事件将调用一次listener1处理程序。

在我看来,你应该离开事件监听器,只有在加载了所有资源后才删除它,并使用管理器来控制对资源加载的访问,这样它就是集中的,所有的ResourceEvent.LOADED事件都会由listener1函数/方法处理。 然后是cource,如果您的应用程序在其整个生命周期内加载资源,请不要删除该侦听器,只在您不再需要它时将其删除一次。

我不是百分百肯定我理解你的意思所以我希望我不是这里的主题! 希望这会有所帮助。

答案 3 :(得分:0)

我认为在这种情况下使用匿名侦听器没有任何优势(但在某些其他情况下可能更清晰,但我认为你在这里没有任何收获)。

无论如何,有几点需要注意:

1)你的第二个选择是要求麻烦。不要对匿名函数使用弱引用。可以在调度事件之前收集您的侦听器。 (我说不要使用弱引用期,但在大多数情况下是个人品味;但在这种情况下,它不是。)

2)只要您可以确定侦听器将始终触发并且这是将被分派的唯一事件,此方法将起作用。如果您的调度员发出一些事件来发出错误信号,那么您的听众将不会被删除。

3)删除侦听器不一定与垃圾回收有关。在某些情况下,不删除侦听器将导致泄漏(例如,在监听舞台时)。但是,在大多数情况下,不删除侦听器不会导致泄漏(即使使用强引用)。我知道你可能想删除监听器,因为你不再想听一些事件,这是正确的,但我只是添加了这一点。

答案 4 :(得分:0)

感谢所有提示!我现在通过创建一个简单的映射来使用稍微更复杂的解决方案,即加载和失败处理程序由资源ID映射。这是基本的想法:

这取代了原来的代码:

private var _listeners:Object = {};

public function load(resourceID:String):void
{
    ...

    if (loadedHandler != null || failedHandler != null)
    {
        _listeners[resource.id] = new ListenerVO(loadedHandler, failedHandler);
        if (loadedHandler != null)
        {
            resource.addEventListener(ResourceEvent.LOADED, onResourceProcessed);
        }
        if (failedHandler != null)
        {
            resource.addEventListener(ResourceEvent.FAILED, onResourceProcessed);
        }
    }

    ...
}

这是新的事件处理程序:

private function onResourceProcessed(e:ResourceEvent):void 
{
    var r:Resource = e.resource;
    var handler:Function;
    r.removeEventListener(e.type, onResourceProcessed);

    if (e.type == ResourceEvent.LOADED)
        handler = ListenerVO(_listeners[r.id]).loadedHandler;
    else if (e.type == ResourceEvent.FAILED)
        handler = ListenerVO(_listeners[r.id]).failedHandler;

    if (handler != null)
    {
        _listeners[r.id] = null;
        delete _listeners[r.id];
        handler(r);
    }
}

一个小型的包级VO,用于保持所有强类型......

class ListenerVO
{
    public var loadedHandler:Function;
    public var failedHandler:Function;

    public function ListenerVO(lh:Function, fh:Function)
    {
        loadedHandler = lh;
        failedHandler = fh;
    }
}

让我知道你对这个想法的看法!