LA Flash and Flex Developer
18 Nov
Josh Tynjala and I were speaking today, well actually typing over IM. We were discussing a problem we both ran into recently, the fl.transitions.Tween class was not dispatching events consistently. Sometimes the TweenEvent.MOTION_FINISH event would be broadcasted, other times it would not. Sometimes our programmatic tweens were visually completing, other times they would not, in a few cases they wouldn’t even start. After a little debugging we realized that the AS3 Garbage Collection was killing the tweens. You may be asking why would the garbage collection be doing this? Let’s start out by looking at some example code.
var len:int = _model.getNumberOfItems();
for (var i:int = 0;i<len;i++)>
var clip:MovieClip = _scope.getChildByName('button'+i);
var myTween:Tween = new Tween(clip, 'alpha', Regular.easeOut, 0, 1, 0.5, true);
myTween.addEventListener(TweenEvent.MOTION_FINISH, buttonAnimationComplete);
}
Pretty simple code, in my AS2 days I used code similar to this a million times and never had a problem. All I am doing is looping through a set number of MovieClips and fading them up from 0 to 100%. Most times this worked fine, but a few times the tweens never completed and buttonAnimationComplete was not called. This is because the AS3 Garbage Collection was deleting the Tweens just like it should be. What? The tweens should be deleted? Yes.
Notice that I am storing the reference to my tween in a local variable that gets overridden in every iteration of the loop. This means there is no real reference to the object and when there is no reference to an object it is flagged for removal by the garbage collector. Since there is no real way to know when the garbage collection cycle is going to run the tweens were completing some of the time and other times were being thrown into the back of the big garbage truck that slowly creeps around your code.
The best way to ensure that this doesn’t happen is to store your reference in a class level member. I ended up storing all of my tween references in a class level array. Once all of the tweens were complete I cleared the array. Here is the updated code that I used, the entire class is not shown here, trust me your scroller will thank me.
private var _buttonTweens:Array = new Array();
private var _buttonTweenCompleteCount:int;
private function animateButtons():void {
var len:int = _model.getNumberOfItems();
for (var i:int = 0;i<len;i++)>
_buttonTweens.push(new Tween(clip, 'alpha', Regular.easeOut, 0, 1, 0.5, true));
_buttonTweens[_buttonTweens.length - 1].addEventListener(TweenEvent.MOTION_FINISH, buttonAnimationComplete);
}
}
private function buttonAnimationComplete(e:TweenEvent):void {
buttonTweenCompleteCount++
if (buttonTweenCompleteCount == _model.getNumberOfItems()) {
_buttonTweens = [];
dispatchEvent(new Event('buttonsReady'));
}
}
I am just using Tweens as an example, but you can run into this same issue with any object that has local references that are overridden. For example, I had a Timer event set to iterate every 100 milliseconds and it called loadAsset(), a method that did just what it says, it loaded an asset.
private function loadAsset():void {
_currentItemToLoad++;
var myLoader:Loader = new Loader();
myLoader.addEventListener(Event.COMPLETE, assetLoaded);
myLoader.load(new URLRequest('images/galleryImage' + currentItemToLoad + '.jpg'));
}
private function assetLoaded(e:Event):void {
var holder:Sprite = new Sprite();
holder.visible = false;
holder.addChild(e.target.content);
_scope.addChild(holder);
e.target.removeEventListener(Event.COMPLETE, assetLoaded);
}
Just like the Tween example above my Event.COMPLETE event was not firing all the time. This is because I was storing the reference to the Loader in a local variable that was overridden each time the loadAsset method was called. To get around this I stored all my references in a class level member (an object or an array work nicely).
Hopefully this helps you figure out why you are not seeing certain events in your code, it had me stumped for a bit. Now you know, and as G.I. Joe said “knowing is half the battle”.