Never Ever Use Tween or URLLoader or Loader or Timer or…
Wednesday, August 1st, 2007The Situation
Your Tweens aren’t finishing. Or your URLLoaders won’t load. Or your Timers stop ticking.
The Problem
Consider the following function:
function doStuff():void
{
var x:Object = {};
}
When this method is called, it creates an object and stores a reference to it in the variable x. After the function finishes executing, there are no remaining references to the object, so the Flash Player clears it from memory (as soon as it gets around to it). Of course, this happens no matter what type of object we create.
Unfortunately, there are some situations where this behavior can cause major problems. Objects like Tween, Loader, and Timer are associated with asynchronous processes (animating, loading, etc..) that we expect to complete regardless of whether the objects that originated them are still in the player’s memory.
Sometimes, we’re lucky. Depending on the how many things your application is trying to do at once, the garbage collector might not perform a sweep while our Tweens are playing (or our URLLoaders are loading). Still, it’s impossible to anticipate exactly when the garbage collector will do its thing, and we certainly can’t allow it to decide whether our applications will function correctly or not. Therefore, we can never ever use these classes without storing a reference to each instance we create.
However, when the number of objects is dynamic (or you’re simply making a lot of them), this can get to be a hassle pretty quickly. This problem is illustrated by the following (admittedly outlandish) function, which creates 1000 Sprites and uses 1000 Tweens to fade them out.
function doSomething():void
{
var mySprite:Sprite;
for (var i:uint = 0; i < 1000; i++)
{
mySprite = new ItemInTheLibrary();
this.addChild(mySprite);
mySprite.x = Math.random() * this.stage.stageWidth;
mySprite.y = Math.random() * this.stage.stageHeight;
new Tween(mySprite, 'alpha', None.easeNone, 1, 0, 1, true);
}
}
In its current state, this function just won’t work. (At least not on a MacBook Pro with 2GB of RAM.) All of the Sprites will be created and added to the stage, but few (if any) of the Tweens will play. By not storing any references to our Tweens, we authorize the garbage collector to dispose of them as soon as the Flash Player needs the memory for something else. Pushing each Tween instance into an Array would solve our problem (try it), but then we’d have to worry about removing them from the Array when the animation finished (or else leave 1000 unused Tweens hanging around in our computer’s memory).
The Fix
My solution is a set of classes (GCSafeAnimator, GCSafeLoader, GCSafeTimer, GCSafeTween, and GCSafeURLLoader) that prevent themselves from being garbage collected while their respective processes are underway. For better or worse, each class has the exact same API as its Adobe counterpart. So to create the Tween in the above function, for example, you would type:
new GCSafeTween(mySprite, 'alpha', None.easeNone, 1, 0, 1 true);
Similarly, loading a url would look like this:
var ldr:GCSafeURLLoader = new GCSafeURLLoader();
ldr.load(new URLRequest(‘http://exanimo.com’));
The packaging is also based on that of the Adobe classes (so GCSafeURLLoader is located at com.exanimo.net.GCSafeURLLoader and GCSafeTween is located at com.exanimo.transitions.GCSafeTween). Of course, when an object is no longer performing its task (be it tweening, loading, or whatever), it again becomes eligible for garbage collection.
Download the GCSafe examples and source as a zip file or from my as3 subversion repository (AS3 only).
Most people probably assume that loading a url with Flash’s URLLoader will eventually result in either a “complete” event or an error event of some kind. It also seems reasonable to assume that, if you create a Tween and leave it alone, the animation will play to completion. However, if you aren’t careful, these objects can be snatched up by Flash’s garbage collector before they’ve finished, leaving your application listening for an event that will never come. It’s for this reason that I can’t see myself ever touching Flash’s Tween, URLLoader, Loader, Timer, or Animation classes again. The "GCSafe" versions of each easily fill the shoes of their Adobe counterparts while eliminating the uncertainty brought on by an overzealous garbage collector. They’re available under the MIT License.
