Remove eventListeners automatically in AS3

ActionScript, Code snippets

I hate removing listeners in AS3. So I created a work-around to autoremove them 🙂 I know there are already some solutions for this, but some of them requires a complete new event system and it is just fun to create your own sources. This class overrides the default behavior of the addEventListener function. It stores references of the listeners inside a Dictionary. The class it selfs listens to the REMOVED_FROM_STAGE event, and when it is removed, then it removes all stored events.

UPDATE 11-10-2010: This code below is a bit outdated.
You’d better use this EventManagedSprite and this EventRemover. BTW: This is all included in FlashFlowFactory. This lightweight framework helps you to easily setup a flash website.


package nl.stroep.display
{
import flash.display.Sprite;
import flash.events.Event;
import flash.utils.Dictionary;
/**
* Simple event managing sprite which automatically removes eventlisteners when the sprite is removed from stage. This class should be extended.
* @author Mark Knol
*/
public class EventManagedSprite extends Sprite
{
private var eventsList:/*Array*/Dictionary = new Dictionary();

public function EventManagedSprite()
{
super.addEventListener(Event.REMOVED_FROM_STAGE, onRemoveEventManagedSprite)
}

private function onRemoveEventManagedSprite(e:Event):void
{
super.removeEventListener(Event.REMOVED_FROM_STAGE, onRemoveEventManagedSprite);

for (var type:String in eventsList)
{
var events:Array = eventsList[type] as Array;

for (var i:int = 0; i < events.length; i++) { var eventObject:EventObject = events[i]; super.removeEventListener( type, eventObject.listener, eventObject.useCapture ); //trace("Auto removed listener ", type, eventObject.listener, "from", this); eventObject.listener = null; eventObject = null; if (events.length == 0) delete eventsList[type]; } } } override public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void { super.addEventListener(type, listener, useCapture, priority, useWeakReference); if (!eventsList[ type ]) { eventsList[ type ] = [ new EventObject( listener, useCapture ) ]; } else { eventsList[ type ].push( new EventObject( listener, useCapture ) ); } } } } final internal class EventObject { public var listener:Function public var useCapture:Boolean public final function EventObject ( listener:Function, useCapture:Boolean ):void { this.listener = listener; this.useCapture = useCapture; } }[/as] Make sure every class you add listeners extends this class to make the most out of it. Otherwise you just need to remove the listeners yourself. Let me know what you think about it. Disclaimer: This class does not detect the eventlisteners on the children of the class. These children should extend the EventManagedSprite class too, otherwise you should remove it manually.

13 responses to “Remove eventListeners automatically in AS3”

  1. soimon says:

    Great idea, a very clever solution for this “problem” 🙂
    You could even add the listeners again when the object is added to the stage, in case that you need that.

  2. katopz says:

    sometime i’m not add sprite to stage or add/remove and add them back again should cause problem, you should take a look at casalib for something like this http://casalib.org/

  3. Keigan says:

    What about just using weak references? Isn’t that enough?

  4. Mark Knol says:

    @soimon Thanks for the tips, Ill definitely take a look at casalib! It looks like they provide a better solution on eventmanagement.

    @katopz I first listened to Event.REMOVED but this causes some strange behavior since it looks like this event is called before garbage collection and looks like randomly listeners will be removed.

    @Keigan I always thought using weak references on eventlisteners were bad practice? I can imaging you dont want to use weak refs at all time?

  5. Cool. Very handy indeed.

    A few thoughts though (and you don’t actually have to publish these comments), why are you using a Dictionary (which requires FP10) when an Array would work just as well (and only requires FP9).

    Second, (and this seems to be a very common problem), it will still work in this case, but only use “super.function()” for functions that you override, so you would use “this.removeEventListener()” instead. I’m not sure it makes a difference in your case, but it’s a good practice and tends to bite you in the rear end when you use getter/setters.

    Finally, it would be quite simple making this into a separate class. In fact, I was so bored that I did so. 😉 Albeit, it’s not as smooth and easy to use as just extending your class, but it works in those cases when you really can’t extend MovieClip. Also, I allowed to choose which event you want to remove the listener with (which is not very likely to change, but could be handy I guess…)
    http://iqandreas.isbetterthanyou.org/scripts/actionscript/mknol/EventRemover.as

    Anyway, thanks a lot for the class. Very handy indeed. 🙂

  6. Mark Knol says:

    Hi Andreas, thanks for your reply. I am very open for comments! You are very right, the class can be optimized a bit. The reason I used an dictionary is because I would like to create add a removeByType function later. I don’t tested your class, but I dont think you can push values into an array by strings, right? About the super.function; yes, this should be removed.
    Just curious about your class; Do you think something is wrong with extending classes? I really like the way you created your class, so thanks for that; its a very clever trick. You only forgot to change the constructor name btw.

    ps. Nice domain name 🙂

  7. > I dont think you can push values into an array by strings, right?
    Oops, I meant to use “Object”. And I found several more flaws in the code, and I don’t even have the excuse of working late. 😛

    I fixed the class (I hope)
    http://iqandreas.isbetterthanyou.org/scripts/actionscript/mknol/EventRemover.as

    > Do you think something is wrong with extending classes?
    Nope, no problem at all. Extending classes is very convenient and handy. The problem is if you simply cannot extend MovieClip (for instance, maybe you want to add the functionality to a Flex component, or a Flixel Sprite (not sure how they are set up though), or if the guy in the Art department has stubbornly created his own classes already and you need to extend “BatEnemy” or “JumpPowerup” rather than MovieClip.

    Sadly, Flash lacks the functionality to “attach” functionality to existing classes, so in order to add features without extending you need to do workarounds like the class I linked to. But I believe that extending definitely makes things easier, and should be used when possible.

  8. pvdn says:

    Managing this is a good thing to do, but I think the REMOVED_FROM_STAGE event is a rather arbitrary moment to have your event-listeners removed. What if you don’t want them to be removed when that happens, or what if you want to manage it for non-displayobjects?

    You should take a look at the core classes of the AS3 Temple Library, the correct removal of eventlisteners is one of the things that the library takes care of: http://twitter.com/templelibrary

  9. Mark Knol says:

    Thanks pvdn, I’ll take a look at the templelib. You are right this class is limited to displayobjects. I also found if you manuallly remove listeners, it will stay inside the manager. So I guess it is not finished yet, will update soon.
    (btw, you work at mediamonks?).

  10. You should not store the listener if you set ‘useWeakReference’ to true. This will break the “weakness”. Although I think you shouldn’t use weak reference at all. (Read more about this in my blog post: http://www.tyz.nl/2010/09/28/weak-listeners-are-bad/ )

    But like Pvdn said, you should take a look at the Temple Library. The Temple Library has automatically removing of event listeners already build in. http://code.google.com/p/templelibrary/

    @Andreas Renberg: Dictionary does not require FP10, but FP9: http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/flash/utils/Dictionary.html

  11. pvdn says:

    yes, both me and thijs work @ mm

  12. fannur says:

    i use the better way)

    static public function autoRemoveEventListener(obj:EventDispatcher, event:String, eventFunction:Function, newEvent:String = Event.REMOVED_FROM_STAGE):void{
    var newEventListener:Function = function(e:Event):void{
    obj.removeEventListener(event, eventFunction);
    obj.removeEventListener(newEvent, newEventListener);
    };
    obj.addEventListener(newEvent, newEventListener);
    }

    usage:

    var iconCliclListener:Function = function(e:MouseEvent):void{
    // some code
    }
    levelIcon.addEventListener(MouseEvent.CLICK, iconCliclListener);
    Utilities.autoRemoveEventListener(levelIcon, MouseEvent.CLICK, iconCliclListener);

  13. Mark says:

    Hi fannur, thats a nice solution using a closure. Short and simple, no manager needed in this case. I would choose to use IEventDispatcher instead of EventDispatcher. Downside: You can easily make a mistake when you declare the wrong event-type to the utility. Personally I like the idea of extending so you don’t have to make that extra function call yourself.

    I found the temple library has nice integrated solution of destruction so nowadays I use that. Its build to also work with non-displayobjects.

Say something interesting

Please link to code from an external resource, like gist.github.com.