AS3: Weakly Referenced Listeners

I will be dealing with this in the next installment of my resource management series of articles, but I thought it was important enough to warrant a quick post in the interim.

I’ve been blogging about how important resource management is going to be in ActionScript 3, and also mentioned that we have some new tools to deal with it. One of these new tools is the ability to have weakly referenced event listeners. A weak reference is one that is not counted by the Garbage Collector (ie. it is not counted in reference counting, and it is not followed for mark sweeping). This means that if the only references remaining to an object are weak, it will be available for collection on the next GC sweep.

References associated with event listeners are often forgotten by developers, which normally results in the listener never being removed from memory. This is why weakly referenced event listeners are so handy in AS3 – if you forget to remove the listener, you will not impede the Garbage Collector’s ability to collect the object.

It’s easy to use weakly referenced listeners in AS3. Just set the fifth parameter of an addEventListener call to true:

// params: eventName, listener, capturePhase, priority, useWeakReference
someObj.addEventListener("eventName",myFunct,false,0,true);

I would very strongly recommend getting in the habit of ALWAYS setting your listeners to be weakly referenced. I can’t think of any good reasons to set it to false, so I’m kind of disappointed that it wasn’t set to use weak references by default, although I understand the rationale. The one place weakly referenced listeners fall apart is in the case of anonymous functions:

addEventListener("eventName",function(evt) { ...code... },false,0,true);

In the above example, the only reference to the anonymous function I defined in-line is the weak reference from the event dispatcher. This means the next time the GC sweeps, my function will be removed, and obviously will no longer be called. This can be very confusing to debug, because the indeterminate GC will make the results seemingly random.

Using anonymous functions in this way is bad practice though (both architecturally, and because it leaves you with no way to remove the listener), so I’d still argue for weak references by default. This is a little selfish though, because I know that we are going to have problems in the future with third party content creating strongly referenced stage listeners (and thus never being garbage collected), whereas my team will never encounter the anonymous function issue (unless it’s in debugging third party content).

So please (PLEASE!), get in the habit of always setting your event listeners to be weakly referenced. It’s a little bit of extra typing, but it’ll save everyone (you, your users, anyone that needs to integrate with / consume your content) a lot of headaches.

Kudos to Adobe for providing this functionality!

Grant Skinner

The "g" in gskinner. Also the "skinner".

@gskinner

46 Comments

  1. Great post, Grant!

    I’m having doubts about adding eventlisteners inline with mxml, now. The generated as doesn’t use week references either even with anonymous functions. And in this case, the compiler generates a class member to invoke instead of using the anonymous function.

    I understand that it was a late arrival to the api, so maybe Adobe refine it more for 2.1?

  2. James,

    I’ve done some investigation on the Flex framework, and there definitely seems to be room for improvement from a memory management standpoint. The framework really doesn’t do much (any?) active memory management, and it probably should. It’s less of an issue in Flex than with Flash for the reasons stated in my last post, but I’d love to see some improvements to it.

    Cheers.

  3. I’ve download your tests file and just run it with setting weak reference to true and… events are always dispatched. I don’t understand why ?

    I’ve also tryed to evaluate GC work by tracing FP memory all along execution. I haven’t notice any action of the GC while creating objects in an enterFrame handler. I don’t see any change in the memory except a small increase every 5s.

    For information I trying to build a pseudo “Box Model” in order to create UI components. Then that side of conception is really a big part in the project and, as you notice in your lasts posts, the new graphic structure offer a great flexibility but also great responsabilities in ressources management, especially when you have to deal with large OO structure, where objets are frequently duplicated (to provide a css like skin management with inheritance and also an object level access).

  4. So the Flash Player truely supports week refs. The Dictionary class can also hold items by week ref. But is it possible to create week refs to objects manually like we do in C#?

  5. Arrix,

    Not natively, no. In my next article I will be looking at ways to circumvent this, including source code.

  6. Hi Grant,

    Thanks so much for the series on resource management. This is a really important topic that is all too easy for developers to ignore. Just one thing… I have never heard mark-and-sweep called mark sweeping… seems like a malapropism. Thanks for your awesome articles!

  7. You could be right… mark-sweep is a common term, stretching back to at least the early nineties, but I apparently decided to turn it into a verb. I’ll go back and revise my terminology tomorrow. 🙂

  8. Hello Grant!

    I’m developing a management aplication…The whole application is based on popups and I’ve noticed that when I open a popup a certain memory is being used..I close the popup and guess what…the memory is not given back…

    I quess that the solution would be to set a weak reference. I’ve tried, but i didn’t manage to do that…please help!!!!!!!!!!!!!

    the example could be found here: http://adidre.go.ro/popup/erw.swf

  9. Hi Grant,

    To clarify something. My understanding of automatically bound methods is that they are a bit like anonymous functions, and so if you have a class Bar with a method Bar.foo which handles events:

    // from within Bar

    someObj.addEventListener(“eventName”,foo,false,0,true);

    the bound method foo will be garbage collected if you don’t save a reference to it in your class like this:

    this.listener = methodFoo; // save for later

    someObj.addEventListener(“eventName”,listener,false,0,true);

    Is this correct?

  10. Oops, ‘methodFoo’ should be just ‘foo’ in the above obviously.

  11. Peter O'Brien June 30, 2007 at 6:41am

    Hi Grant, thanks for these articles although I don’t understand the rationale for not making weak references default to true. If they fall apart on anonymous functions, simply set weak references to false when using anonymous functions. Or am i missing something more?

    We need as much rails methodology in the design of APIs as possible to increase fun and productivity in development. (i.e. no more attachMovie is a godsend!)

  12. hello, I’ve been tracking this memory issue and found your articles, very interesting indeed I didn’t knew about the weak references.

    I want to add an example of meaningful annonymous functions as event listeners, they can be used to allow the listener function to receive more parameters, they act as a wrapper that calls the real listener function with the rest of the data because inline functions inherit it’s environment so we can do something like this (Flex example):

  13. Do Loader objects with event listeners attached (eg Event.COMPLETE) persist as long as the load operation is underway? I have code that initializes a Loader within a function with the onComplete listener function nested within that function and it has never failed, but some advise that this practice is risky. I seem to remember AS2’s load functions working the same way as my AS3 example above seems to be working– that they do persist until the loader’s listener event is executed.

  14. Hi Grant!

    Thanks for the these articles on resource management. I just wanna clarify the situation below.

    // in some class member

    var foo:Function = function(e:Event):void

    {

    trace(“foo called”);

    }

    var urlLoader:URLLoader = new URLLoader();

    urlLoader.addEventListener(Event.COMPLETE, foo, false, 0, true);

    urlLoader.load(//something);

    I tried this and some case the complete was never called. So is this same as the anonymous function?

    I know this might not be a best practice.

  15. In my experience, setting the week reference to true is not really helping, because the instance of my object is garbage collected at an unknown time in future. Could be in a second or few minutes. Until then my object in memory will still be listening to events, which i dont want to happen. I have to unregister the listeners explicitely before setting the object referenct to null.

    There is no easy way to do this either, i have know to what events a particular object is listening to, in order to unregister. I cannot get all the listeners registered by an object, there is no API to do that.

  16. Somehow, setting the weak references to “true” actually causes more bug than it fixes for me. I’ve already gotten into the habit of removing the eventListener when I don’t need them. In the case of an EventListener that is regularly added and removed, setting the weak references to “true” randomly prevents the EventListener to being added back at a later time. I don’t understand why, but with with weak references to false (default), this does not happen. What is happening?

  17. Hi Grant,

    I can understand the rationale for this, but doesn’t saying “It’s also an example of bad practice because it doesn’t use weak references for the listener” (one of your comments on http://www.gskinner.com/blog/archives/2008/07/additional_info.html) take it a little bit to far? I mean we have tons of references, which all needs to be cleaned up nicely in order to make garbage collection possible, and removing listeners you added is just one of those? Or am I missing something here. If I was reading someone elses code and not seeing listeners cleaned up, I’d get nervous, since then I would have to check each addListener to see if the references were weak.

  18. Sorv,

    I definitely think developers should explicitly clear their listeners, but it’s something that is very frequently missed, even by the best AS developers. Always using weakly referenced listeners is a good way to ensure that if you do miss cleaning one up, it isn’t going to prevent collection.

    Think of it as turning on the alarm and locking your doors – it provides an added level of security.

  19. Loader objects and NetConnections do not receive event listener calls if you set useWeakReference to “true.” I’ve encountered this problem multiple times. What do you think Adobe’s thought behind this may be?

    – TK

  20. I’ve write a little EventManager to clean all event associate to an object (a whole swf can be this object), it’s a little bit ugly right now, but does it make sens ? :

    Class :

    package

    {

    import com.utils.*;

    import game.*;

    public class EventManager

    {

    private static const DEBUG = false;

    private static var events:EventsHolder = null;

    public function EventManager()

    {

    }

    public static function Add(s, o, e, f) // swf, object, event, function

    {

    if(DEBUG)

    {

    trace(“s”, s);

    trace(“o”, o);

    trace(“e”, e);

    trace(“f”, f);

    }

    if(!events) events = new EventsHolder();

    var ed:EventDescriptor = new EventDescriptor();

    if(!events[s]) events[s] = new Array();

    ed.object = o;

    ed.event = e;

    ed._function = f;

    events[s].push(ed)

    o.addEventListener(e, f, false, 0, true);

    }

    public static function Remove(o, e, f)

    {

    o.removeEventListener(e, f);

    }

    public static function RemoveAllForSwf(s)

    {

    if(!events[s]) return;

    while(events[s].length)

    {

    var e:EventDescriptor = events[s].pop();

    if(e.object.hasEventListener(e.event)) e.object.removeEventListener(e.event, e._function);

    }

    }

    }

    }

    internal dynamic class GameData

    {

    public function GameData()

    {

    }

    }

    internal dynamic class EventsHolder

    {

    public function EventsHolder()

    {

    }

    }

    internal dynamic class EventDescriptor

    {

    public function EventDescriptor()

    {

    }

    }

    How To Use :

    public function ClassXXX()

    {

    DataManager.Instance();

    EventManager.Add(this.name, this, Event.REMOVED_FROM_STAGE, onRemovedFromStage);

    EventManager.Add(this.name, this, Event.ADDED_TO_STAGE, onAddedToStage);

    }

    private function onRemovedFromStage(e:Event)

    {

    EventManager.RemoveAllForSwf(this.name);

    }

    private function onAddedToStage(e:Event)

    {

    }

  21. if you set the weak reference to true, do you still need to remove the event listener, or it is not necessary any more?

    Thanks in advance for your reply.

  22. Jaime it shouldn’t be necessary to remove the event listener yourself in case of a weak reference BUT there seems to be a bug in Flash Player 9. It should have been fixed in FP10 according to Ted Patrick (see comments @ http://www.onflex.org/ted/2008/09/useweakreferencesboolean-false.php) but I have the impression that the problem is still there…

  23. I understand about cleaning up after yourself.. But, if i was creating a video application and i needed the to know when events were fired at any time during the playback shouldnt the event listeners always be there? or should i set the weakref to true?

    Hope this makes since..?

    mitch

  24. Nicolas Guionnet October 31, 2008 at 12:46pm

    I came to your article cause I had a serious memory problem in one of my projects …

    I read your article, I followed the advice and then …. my problem were gone …

    Weak Reference ! … so simple and usefull …

    I searched in all my file for every single addEventListener and added the precious fifth argument (“true”) … waouuuh ! GREAT !!

    If a could send you a beer by Email … I would …

    Thanks A LOT !

    Nicolas

  25. Nicolas Guionnet November 1, 2008 at 4:11am

    Well, I wrote the last coment in a state of total enthousiasm. When I went back to my project, I noticed that some of the eventListeners were unable to triger since I had set the useWeakReference property to true. Seting it back to false solved this problems !

    I have now no way to solve this …

    I decided to remove all of them manually in a ‘dispose’ method. (It’s a lot of boring work !)

    I’m sad thad what I thought was a good and light solution to the problem has indeed its drawback…

    Any Idea ?

  26. Grant,

    I’ve always used this technique when attaching listeners. However, recently after I removed a sprite with over, out, and release listeners attached, the out listener kept firing after the symbol had been removed from the stage. The only solution I’ve seen to force the weak reference argument to always function properly is to set the reference of the movieclip to null (this invokes garbage collection perhaps?). I am working on researching this more as I would love to have to not worry about doing anything except for setting this argument to to true.

    At the moment from what I’ve read the only 100% stable solution would be to code a function that gets called after the symbol is removed from the stage, using the REMOVED_FROM_STAGE event, then to loop through all the symbol’s listeners and remove them. Not sure if this is possible yet though…

    Any insight into the matter would be much appreciated.

    Many thanks & Happy Holidays,

    Julian

  27. From this article:

    “A weak reference is one that is not counted by the Garbage Collector (ie. it is not counted in reference counting, and it is not followed for mark sweeping). This means that if the only references remaining to an object are weak, it will be available for collection on the next GC sweep. ”

    So I assume it is NOT(!!) a good practice to use weak referenced listeners since they will be removed with the next garbage collection cycle (which could also be in the next millisecond -you never know!)

    Is this right?

  28. Mike – from the article: “So please (PLEASE!), get in the habit of always setting your event listeners to be weakly referenced.”

    The rationale is explained in the article. Let me know if you have specific questions.

  29. I’m having trouble fixing an issue with a gotoandlearn tutorial that involves a video playing within a MovieClip that is spun in 3D.

    http://theflashblog.com/flash/3dflip.html

    http://www.gotoandlearn.com/play?id=91

    At least 1 other person within the comments is having the same problem, where after the movie is spun back, the controls for the scrubber no longer respond. I’m spinning my own Movieclip and am experiencing the same loss of event listeners. Could this be an issue of event listeners being garbage collected? I’m just now trying to understand this topic. Thanks

    Jim

  30. i don’t understand why you set your event to weak if you can remove it with removeEventListener.

    can anyone show me how weakListener works. i don’t really catch the concept.

    tqvm

  31. As I understand it, by default if an object has an Event Listener it will not be removed from memory, this means if you forget to remove a listener, the Object will still exist in memory (even if it’s been removed and set to null).

    By using a weak listener you change the default behaviour: the listener will not prevent the object from getting removed from memory.

  32. From the Adobe docs:

    Class-level member functions are not subject to garbage collection, so you can set useWeakReference to true for class-level member functions without subjecting them to garbage collection.

    If you set useWeakReference to true for a listener that is a nested inner function, the function will be garbage-collected and no longer persistent. If you create references to the inner function (save it in another variable) then it is not garbage-collected and stays persistent.

  33. Do you regret this post? You had good intentions, but we’re ALL humans and will chatter! I think the post from the Adobe Doc clarified everything.

    Good work Grant. I’m a personal fan!

  34. In a class I used:

    attractTimer = new Timer(1000);

    attractTimer.addEventListener(TimerEvent.TIMER, checkAttract, false, 0, true);

    attractTimer.start();

    Using weak reference here prevents checkAttract() from ever being called. Took me a while to find this, as I’ve been using weak ref for a while.

    A simple:

    attractTimer.addEventListener(TimerEvent.TIMER, checkAttract);

    Works as expected.

    ?

  35. @Dave

    When using weak references, if the attractTimer variable is declared within your function, then when the function completes and the variable goes out of scope, there will no longer be any references to attractTimer, and it will (eventually) be garbage collected.

    By not using a weak reference, EventDispatcher still has a reference to attractTimer, and thus it will not be collected when the function completes.

    A solution is to declare an instance variable that holds a reference to attractTimer and use weak references. Then clean up the variables and event listeners when they are no longer needed.

    You dont show all of the code, but this could be what is causing your issue.

    mike chambers

    mesh@adobe.com

  36. I got in the habit of always using weak references and then (painfully) learned to be more careful. If the only reference to the object that added the listener was in a local variable that goes out of scope, the listeners disappear immediately. This is to be expected but it isn’t always obvious. In such cases, you must either use strong references or make sure that there’s at least one reference to your object when the event occurs. Local variables usually go out of scope before your event arrives, but an instance variable should work (unless your instance is collected).

  37. Hi Grant!

    I never use weak references, because I think it’s not a good programming-style. I also think that this may cause some strange errors when a needed listener is removed. Instead of this I wrote a ListenerProxy-Class that keeps track of the listeners registered on an object. You can also simply add/remove multiple listeners on multiple objects. You may have a look at my website and try it out.

    Regards,

    Daniel

  38. Hello!

    Thanks for this lesson (but especially for the info about the GC I thank you..).

    A quick question: When we dispatch events, I understand the Event object references a ‘target’ and a ‘currentTarget’.

    Are those references weak or strong?

    Bye, Oli.

  39. Oliver – those are strong, but they are also transient (ie. they only exist while the event is being dispatched), unless you hold a reference to the event (which would be unusual).

  40. Keep it coming Mr Skinner very useful indeed.

  41. From the Flash Help:

    useWeakReference:Boolean (default = false) — Determines whether the reference to the listener is strong or weak. A strong reference (the default) prevents your listener from being garbage-collected. A weak reference does not.

    It’s the opposite of what your saying. Or am I missing something?

  42. just dont use anonymous functions. i was using them till i had big trouble with the remove.

    your code will look much better if you dont use. just put a global param:Object into your class. then you can call anything when the event dispatches e.g e.target.param.callback(e.target.param.data).

  43. So I posted this on my site (below) however i figured it might be useful here. It’s for removing an anon function:

    Hey guys and gals, quick lesson in flex. One popular thing to do is use something called an anonymous function with eventListeners when you want to pass the function something more than just the event. You would typically just say:

    myObj.addEventListener(Event.ENTER_FRAME, function(a_event:Event):void{someotherFunc(a_event, additionalArgs);});

    But this sucks if you are going to want to remove the listener. So something you can do is the following. You can create a function variable (func), then set the anonymous function to this var. Then the second function that you ultimately want to call (func2) will have an optional parameter ‘myHackFunc’ that will be the variable that you created (func). Then you just call remove event listener on ‘myHackFunc’.

    I have heard of other ways of doing this, mainly, using ‘arguments.callee’ This causes my app to die, but the way described above, and shown below, seems to work all the time. Hope it helps!

    public function myFunc1():void{

    var func:Function = function(a:Event):void{ func2 (a, some_args, func)};

    i.addEventListener(Event.ENTER_FRAME, func);

    }

    public function func2(a_event:Event, some_args:*, myHackFunc:Function = null):void{

    a_event.currentTarget.removeEventListener(Event.ENTER_FRAME, myHackFunc);

    }

    You can read the whole thing here: http://constipatedkoala.andrewluly.com/2009/04/removing-anonymous-function-from-event.html

  44. Oliver – those are strong, but they are also transient (ie. they only exist while the event is being dispatched), unless you hold a reference to the event (which would be unusual).

  45. Great post! Still relevant.

  46. I prefer weak refs myself, but can think of at least one situation where a strong ref helps implement good design. It’s something I call ‘translate event’, it raises the semantic level of a view event like this:

    static public function translateEvent(type:String, newEvent:Event, target:DisplayObject):void {
    target.addEventListener(type, function(e:Event):void {
    target.dispatchEvent(newEvent);
    });
    }

    Used like so:
    translateEvent(MouseEvent.CLICK, MyViewEvent.PLAY_GAME, thePlayButton);

    In these cases I never want to remove the listener for the lifecycle of the button. I always want to hear PLAY_GAME not just CLICK. Strong ref helps implement this in a simple way.

    Note: as3 EventDispatcher.dispatchEvent clones the event. So if doing something like this, implement clone on event subclasses that define their own properties.

Leave a Reply

Your email address will not be published. Required fields are marked *