Anonymous functions as event handlers in Action Script - good or bad?
I came to AS3 from JS world, and I should confess that anonymous functions are my weakness. I tend t开发者_如何学JAVAo 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?
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();
}
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;
}
}
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! ;)
精彩评论