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

发布于 2024-10-07 02:03:09 字数 1207 浏览 0 评论 0原文

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

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

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

contorls.elapsed是setter,除了设置视频播放器的当前播放时间之外,还更新整个UI,而streamNetStream 对象,用于传输实际视频。

在很多其他地方,匿名函数可以使代码更简洁、更直观。检查以下代码以了解控制栏的简单淡入效果:

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 后它们就会自动删除?是否可以正确删除具有匿名函数的侦听器?

I came to AS3 from JS world, and I should confess that anonymous functions are my weakness. I tend to use them everywhere. Now, coming to AS3 I've heard and read in lots of places, that AS and Flash are enormously bad at handling garbage collection, that one should empty, dispose and remove all event handlers and objects manually to avoid weird and unexplainable memory leaks and crashes. Not sure what part of this is true, but I would like to follow best practices right from the beginning.

So my question would be - how bad is idea of using anonymous functions as event handlers? Consider for example a code like this:

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

contorls.elapsed is the setter, which apart from setting current play time for video player, updates the whole UI, and stream is NetStream object, which streams the actual video.

There are lot's of other places where anonymous function may make code cleaner and more intuitive. Check the following code for simple fade-in effect for the control bar:

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();
        }
    }

I totally like how it looks and fits into the code, but I'm concerned about leaks. While Event.ENTER_FRAME handler probably would never become harmful in this form, what about timer listeners. Should I remove those listeners manually, or they will be removed automatically, as soon as I set fadeTimer = null ? Is it possible to remove listeners with anonymous functions properly at all?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

马蹄踏│碎落叶 2024-10-14 02:03:09

刚刚注意到这篇文章——有一些东西可能对你有用。一种是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();

}

Just noticed this post -- there are a couple things that might be of use to you. One is arguments.callee (which is a reference to the current function you're in). This is useful for removing references in anonymous functions. Also, it could be noted that you could use weak references in your addEventListener code -- however, this won't work for variables that are anonymous, as they'd get GC'd pretty much immediately. For simplicity sake I rewrote your code like this: (should work -- haven't tested)

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();

}
沫离伤花 2024-10-14 02:03:09

我会做这样的事情。并且,当您想确保在中断时清除计时器时,请务必使用 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;
    }
}

I would do it something like this. And, be sure to use dispose() when you want to make sure to clear the timer if interrupting.

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;
    }
}
爱的故事 2024-10-14 02:03:09

在函数方法有效的地方使用函数方法并没有什么问题。就内存泄漏而言,您需要跟踪该对象到舞台上,看看是否可以将其删除。

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

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

如果一切都失败了,请使用分析器看看! ;)

There's nothing wrong with using function methods where it works. As far as memory leaks go, you need to track the object to the stage to see if it can be removed.

Adding an ENTER_FRAME event handler to the control ensures that the control has a reference to the anonymous function. As the code is part of the control (or so it appears), this is fine as the anonymous function will be removed when the control is.

Adding an event handler to the timer ensures that the timer has a reference to the anonymous function. If the timer is running, it will keep the anonymous function reference alive and, by association, the enture control. Once the timer has stopped, however, both it and the function should be collected.

If all else fails, use the profiler and see! ;)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文