I heard about a class that Grant Skinner wrote called Janitor that was supposed to help keep track of listeners, but I couldn’t find it. Consequently, I wrote my own “EventManager” class for a gaming project I’m working on which keeps track of Event listeners in a project.
As all Actionscript 3 developers know, one of the biggest annoyances is keeping track of listeners and ensuring objects are collected in memory. This class does a lot of that for you, and even has a method removeAllListeners which has different filters. So let’s say some listener keeps calling a function, you can remove all listeners that point to that function. Or let’s say you want to remove ALL key listeners.
Here it is:
*EDIT 9/30/08: EventManager Updated. Click here to get it.
For example, let’s say you have something like so:
var obj:MovieClip = new MovieClip(); var obj2:MovieClip = new MovieClip(); obj.addEventListener(Event.ENTER_FRAME,Test,false,0,true); obj = obj2; obj.removeEventListener(Event.ENTER_FRAME,Test); // Does NOTHING!
Test will still be called every frame! Even though weak reference is set to true and there are no more references to obj! This is not good! But with EventManager…
var obj:MovieClip = new MovieClip(); var obj2:MovieClip = new MovieClip(); EventManager.addEventListener(obj,Event.ENTER_FRAME,Test,false,0,true,true); // last parameter actually adds the listener, see documentation in class of why this last parameter exists. obj = obj2; // Now we have a bunch of options, any of the bottom lines would work EventManager.removeAllListeners(null,Event.ENTER_FRAME); // will remove all Event.ENTER_FRAME listeners EventManager.removeAllListeners(null,Event.ENTER_FRAME, Test); // will remove all Event.ENTER_FRAME listeners that call Test EventManager.removeAllListeners(null,null, Test); // will remove all listeners that call Test EventManager.removeAllListeners(); // will remove all listeners
The only problem with the class is that this version does not distinguish useCapture events… But big deal. Maybe I’m naive, but how often does anyone actually set useCapture to true?
Anyway, if this class gets a lot of attention I’ll put it as open source on Google Code. Then someone else can add in functionality for useCapture.
This class was such a pain to code. AS3’s Dictionary is the only sure fire way to index objects as objects (as Array uses the result of toString() for indexes), but because (for some reason that’s beyond me), Adobe decided not to have a length property in Dictionary, my life was made very difficult. There might be some bugs, but based on my initial tests everything seems to be working fine. I wonder how Grant’s Janitor class is compared to mine. I really couldn’t figure out any other way to write the function definitions for EventManager.addEventListener and EventManager.removeEventListener (having actuallyAddListener and actuallyRemoveListener as the last parameter). I wish AS3 had a way to get the memory location value of an object. Something like Object.toMemoryString or something. That way Arrays could be used and the Object.toMemoryString value (which would be unique for every single object) could be used as keys.
If you decide to use this class, please let me know so I know my work hasn’t been in vain!
I’m not entirely certain I released the Janitor class… I’ll have to check and release it if not. It also makes extensive use of weakly referenced dictionary objects.
Just for the record, it’s “Grant”, though “Grank” would be a pretty interesting name. 🙂
Doh, that was a typo.
Great class. Even though using weak references is mostly sufficient, I like the ‘removeall’ function just for the convenience.
Do you mind if I extend it slightly to support passing of 1-2 variables ? Sometimes it can be very handy to pass params via events.
thanks!
Erik,
Weak references are sometimes efficient; however, they don’t always work (see example in post).
I have no problem of anyone extending the class; I could even post it on google code if anyone else wants to contribute to it.
Danny – Thanks. This is exactly what I have been searching for! I’ve got lots of vendors that aren’t cleaning up listeners very well….
The Janitor Class can be found in the source files included with this presentation: http://gskinner.com/talks/resource-management/
Super useful class. I do a lot of work loading files from animators that love ENTER_FRAME in nested MovieClips. It’s gotten to the point where a lot of times with AS3 I am reloading the player just to avoid the cleanup that is involved in getting these files out of the player. Being able to hand them something like this off so that I can keep track of their stuff in the shell is invaluable. Thanks for putting it up there.
note removeAllListeners
var observers:Array = (temp as Array).slice( 2 );
while( observers.length > 0 )
{
removeEventListener(temp[0], temp[1], observers[0], false );
observers = Arrays.removeAt(observers, 0);
}
If you work consistently, another very clean and effective approach is making objects independent.
We can use destructors, despite the fact that they are not native to AS3.0.
Example:
package
{
import flash.display.Sprite;
import flash.display.DisplayObjectContainer;
import flash.events.Event;
import flash.events.MouseEvent;
public class Test extends Sprite
{
pubic function Test( autoAttachTo:DisplayObjectContainer = null )
{
if( autoAttachTo )
autoAttachTo.addChild( this );
addEventListener( Event.ENTER_FRAME, process );
addEventListener( MouseEvent.CLICK, click );
}
public function Destructor()
{
removeEventListener( Event.ENTER_FRAME, process );
removeEventListener( MouseEvent.CLICK, click );
if( parent )
parent.removeChild( this );
}
private function process( e:Event )
{
trace(“enterframe”);
}
private function click( e:Event )
{
trace(“clicked”);
}
}
}
When I instantiate one of my classes, I generally need only this:
var test:Test;
test = new Test( stage );
When I trash it, I do this:
test.Destructor();
test = null;
Creating and deleting objects is now more consistent and simple. Object-specific tidying up (such as removing listeners) is done where it should be: inside the object.
The destructor is the effective opposite of the constructor, plus it removes any other references and listeners created throughout the class file. If its keep your destructor up-to-date, your class can hold its own 🙂
Aww, sorry, the indention was lost 🙁
Makes it look like shit doesn’t it 😉
Timo,
Sure. I like many others use this design philosophy all the time. I personally call it “destroy” rather than destruct.
I still do it even though I use the EventManager.
How about overriding addEventListener in your objects to save all the event listeners….
Pseudo:
override protected function addEventListener(….):void
{
putEventListenerOnList(…..);
super.addEventListener(…);
}
removeAllEventListeners()
{
for (listeners on listenerlist) removeEventListener(….);
}
Larry,
That’s a lot of work… EventManager can just be used to track all of them. There’s no real reason to encapsulate destroy/destruct functions if all they do is remove event listenrs. If they do more then that’s another story.
Well… Why FDT pukes me much much much errors…
Well… FDT pukes me much much much errors…
Well, I’m kinda newbie, so please confirm what I think is wrong.
In the code, [CODE]&&[/CODE] is replace by [CODE]&&[/CODE]Probably due to the HTML parser. It fixes the errors…
Gad damn, sorry…
& is replaced by &+amp;
May I read…
/*
*Fixed bug in remove all listeners (accidently put && instead of ||)
*/
If you look at the source code there is a link that says “View plain” on the top left.
Thanks for sharing the Class, its help me solve a right headache
Cheers
Thanks for the class! It is really helpfuL!
Great thanks for the class. Well done, clean and useful code.
Danny, what you think about to do something like that:
EventManager.addEventListeners( loader, [
Event.COMPLETE, onLoaderComplete,
ProgressEvent.PROGRESS, onLoaderProgress,
Event.OPEN, onLoaderOpen
], true, 0, true );
method signature should be something like this:
EventManager.addEventListeners( obj:IEventDispatcher, eventListeners:Array, useCapture:Boolean = true, priority:int = 0, useWeakReference:Boolean=true, actuallyAddListener:Boolean = true):void
handling eventListeners parameter ever by pairs, could be usefull to manage when a lot of events should be applied to the same object. (forgive any english mistake =P)
Love the idea Dennin, and I added it.
Only briefly tested – but so far does exactly what I need – Fantastic class – thanks for sharing 🙂
Adobe please add: removeAllListeners i know its a bit of an overkill but seems easier than fixing the memory issues flash has ;P
ohh yea nnd thanks for the class 🙂 looking forward to trying it out
Dear Danny,
Thank you very much, You rescued my time.
This weblog is simply excellent, I assumed I do know a whole lot, but I’m so mistaken, like the prior saying the much more you already know, the additional you obtain out how little you know. Thanks for the info.
do it’s support for EventManager.removeAllListeners(obj,Event.ENTER_FRAME, null); ???
thx in advance…
yogibali:
it should support that. that command should remove all event enterframe listeners for that object