views:

282

answers:

4

I've been teaching myself actionscript 3 over the past month or so and recently ran into an issue where an object kept doing things after I thought it had been removed. I figured out that the problem was caused by an event listener using the default of useWeakReference = false and I'm wondering why that's the default. what is the advantage to not using a weak reference? and why is that the default? it seems to me that in general you'd want to use weak references, so I must be missing something.

Thanks, -Ted

+2  A: 

Hi Ted,

You're right, typically you do want to use weak references, probably about 99% of the time. I think the default value really should be set to true, but we just have to deal with it not being so. I suggest that you get in the habit of always using weak references, and you will save a few headaches, and see some performance benefits as it helps with garbage collection management.

So when would you want it to be false? Basically any time you don't want things automatically clean up when the garbage collector decides you're not using it anymore. These situations are rare, and when the time comes you'll probably just know.

HOWEVER That might not solve your particular problem on it's own. The garbage collector only runs at unpredictable intervals, so even though you remove all references to an object, it might still handle these events until it is cleaned up. Your best bet is to remove the event listeners yourself to guarantee they will not be triggered again. You can do that with the removeEventListener method.

Good luck,

Tyler.

Tyler Egeto
+5  A: 

the point is, that weak references are expensive ... they are both slower and consume more space ... here is some benchmark code:

package {
    //{ region imports
     import flash.display.Sprite;
     import flash.events.Event;
     import flash.events.EventDispatcher;
     import flash.system.System;
     import flash.utils.*;
    //} endregion
    public class Main extends Sprite {  
     public function Main():void {
      switch (0) {
       case 0: this.benchmarkDispatchers(false); break;
       case 1: this.benchmarkDispatchers(true); break;
       case 2: this.benchmarkDictionaries(false); break;
       case 3: this.benchmarkDictionaries(true); break;  
      }
     }
     private function benchmarkDictionaries(weakKeys:Boolean, size:uint = 1000000):void {
      var a:Array = [];
      for (var i:int = 0; i < size; i++) 
       a.push( { "foo":i } );

      var d:Dictionary = new Dictionary(weakKeys);
      var start:int = getTimer();
      var mem0:int = System.totalMemory;

      for (var j:int = 0; j < size; j++) 
       d[a[j]] = j;
      trace("adding "+size+" keys took "+(getTimer()-start)+" msecs and "+(System.totalMemory-mem0)+" B of memory with weakKeys == "+weakKeys);       
     }
     private function benchmarkDispatchers(weakRef:Boolean, size:uint = 100000):void {
      var a:Array = [];
      var f:Function = function (i:*):Function {
       return function ():void { i; }
      }
      for (var i:int = 0; i < size; i++) 
       a.push( f(i) );
      var e:EventDispatcher = new EventDispatcher();
      var start:int = getTimer();
      var mem0:uint = System.totalMemory;
      for (var j:int = 0; j < size; j++) 
       e.addEventListener("foo", a[j], false, 0, weakRef);
      trace("adding " + size + " event handlers took " + (getTimer() - start) + " msecs and " + (System.totalMemory - mem0) + " B of memory with weakKeys == " + weakRef);
     }
    }
}

this is, what i get on my machine:

adding 100000 event handlers took 679 msecs and 6922240 B of memory with weakKeys == false
adding 100000 event handlers took 1348 msecs and 13606912 B of memory with weakKeys == true
adding 1000000 keys took 283 msecs and 16781312 B of memory with weakKeys == false
adding 1000000 keys took 906 msecs and 42164224 B of memory with weakKeys == true

results are a little more drastic for dictionaries, most probably because there are no ActionScript calls involved, concerning time, and since some storage overhead in event handler registering lessens the difference between memory needed (as you can see, it's 69 Byte/handler and 16 Byte/key, when comparing weak references) ...

so yeah, it is about performance ... using weak references is not about the cool fact, that you don't have to remove the listener in order for an object to die ... if you want to have a scalable app, you need to do these kind of things yourself, and if you want it to be 100% reliable, you can't hope for the GC to do your job, but you need tomake cleanups yourself ... and also, if you have a good hierarchy in your app, you will probably not run into this problem much to often ... in a sense, it is a luxury, if you don't wanna spend you time doing proper object cleanups, because problems that cannot be solved without weak references are rare ... it should be used when it offers a real advantage, not just out of laziness ... i think that is why it is false by default ...

hope that helps ... ;)

back2dos
A: 

Weak references can bite you when doing asynchronous operations, since anything that is not executing for tethered to the object graph can be garbage collected at any moment.

Particularly, never use weak references for event handlers that are scoped functions:

function weakRefSample() : void
{
    var evntDispatcher : IEventDispatcher = new EventDispatcher();

    evntDispatcher.addEventListener(Event.COMPLETE, scopeHandler, false, 0, true);

    function scopedHandler(event : Event) : void
    {
    }
}

There are two things that go wrong in the above example:

  • scopeHandler can be collected because there it is not tethered to the object graph
  • the execution scope of weakRefSample can be collected because nothing requires it to exist (nothing is using scopedHandler), so evntDispatcher can be collected. If evntDispatcher was waiting on another asynchronous event, it might be collected before that event completes.
Richard Szalay
A: 

I have found that Timer instances which have weak referenced event listeners sometimes do not fire the event.

Jeremy White