匿名函数作为Action Script中的事件处理程序 - 好还是坏?

时间:2010-12-12 09:45:58

标签: actionscript-3 event-handling anonymous-function

我从JS世界来到AS3,我应该承认匿名函数是我的弱点。我倾向于到处使用它们。现在,来到AS3我已经听过很多地方的内容,AS和Flash在处理垃圾收集时非常糟糕,应该清空,处理和删除所有事件处理程序和对象,以避免奇怪和无法解释的内存泄漏崩溃。不确定这部分是什么,但我想从一开始就遵循最佳实践。

所以我的问题是 - 使用匿名函数作为事件处理程序的想法有多糟糕?考虑一下这样的代码:

addEventListener(Event.ENTER_FRAME, function() : void {
    controls.elapsed = stream.time;
});

contorls.elapsed 是设置者,除了设置视频播放器的当前播放时间外,还会更新整个用户界面, NetStream object,它传输实际视频。

有很多其他地方匿名功能可以使代码更清晰,更直观。检查以下代码以获得控制栏的简单淡入效果:

public function showControls() : void
    {
        var self:Controls = this;

        if (!visible) {
            visible = true;
            fadeTimer = new Timer(30, 10);
            fadeTimer.addEventListener(TimerEvent.TIMER, function() : void {
                self.alpha += 0.1;
            });
            fadeTimer.addEventListener(TimerEvent.TIMER_COMPLETE, function() : void {
                self.alpha = 1;
            });
            fadeTimer.start();
        }
    }

我完全喜欢它的外观和适合代码,但我担心泄漏。虽然Event.ENTER_FRAME处理程序可能永远不会在这种形式中变得有害,但计时器监听器呢。如果我设置 fadeTimer = null ,我应该手动删除这些侦听器,还是会自动删除它们?是否可以正确删除具有匿名功能的侦听器?

3 个答案:

答案 0 :(得分:7)

刚刚注意到这篇文章 - 有几件事可能对你有用。一个是arguments.callee(它是对你当前函数的引用)。这对于删除匿名函数中的引用很有用。此外,可以注意到可能在addEventListener代码中使用弱引用 - 但是,这对于匿名变量不起作用,因为它们几乎立即得到了GC。为简单起见,我重写了这样的代码:(应该工作 - 没有测试过)

private function showControls() : void {

    if (visible) {
        return;
    }

    var self:DisplayObject = this;

    var fadeTimer= new Timer(30,10);
    var handler = function(e:Event) {

        switch (e.type) {

            // timer complete
            case TimerEvent.TIMER_COMPLETE:

                // remove references to this anonymous function -- for garbage collection
                fadeTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, arguments.callee);
                fadeTimer.removeEventListener(TimerEvent.TIMER, arguments.callee);

                // break out
                return self.alpha = 1;

            // timer
            case TimerEvent.TIMER:
                return self.alpha += 0.1;

        }
    }

    fadeTimer.addEventListener(TimerEvent.TIMER, handler);
    fadeTimer.addEventListener(TimerEvent.TIMER_COMPLETE, handler);
    fadeTimer.start();

}

答案 1 :(得分:3)

我会这样做的。并且,如果要确保在中断时清除计时器,请务必使用dispose()。

private function showControls() : void
{
    if(_isVisible)
        return;

    // start you control here
    _fadeTimer = new Timer(30, 10);
    _fadeTimer.removeEventListener(TimerEvent.TIMER, updateFade);
    _fadeTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, updateFadeComplete);
    _fadeTimer.start();
}

private function updateFade(event : TimerEvent) : void
{
    // update fade here
}

private function updateFadeComplete(event : TimerEvent) : void
{
    dispose();
}


private function dispose() : void
{
    if(_fadeTimer)
    {
        _fadeTimer.stop();
        _fadeTimer.removeEventListener(TimerEvent.TIMER, updateFade);
        _fadeTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, updateFadeComplete);
        _fadeTimer = null;
    }
}

答案 2 :(得分:2)

使用函数方法可以正常工作。就内存泄漏而言,您需要跟踪舞台上的对象以查看是否可以删除它。

向控件添加ENTER_FRAME事件处理程序可确保控件具有对匿名函数的引用。由于代码是控件的一部分(或者它出现),这很好,因为当控件被删除时,匿名函数将被删除。

向计时器添加事件处理程序可确保计时器具有对匿名函数的引用。如果计时器正在运行,它将使匿名函数引用保持活动状态,并通过关联保持enture控件。但是,一旦计时器停止,它和功能都应该被收集。

如果所有其他方法都失败了,请使用探查器并查看! ;)