Be a good SWF citizen, listen for Event.UNLOAD

If you have a SWF that you know will be loaded into a parent SWF, then it’s your responsibility as a developer to properly clean up after yourself when your child SWF is unloaded. Certain activities will keep your SWF in memory long after the unload() method is called on the Loader object that holds your child SWF, and there’s a good chance that you’ll have created a memory leak if you don’t take care of things properly.

It’s important to note that in Flash Player 10, Loader has a new unloadAndStop() method that does some of this work for you (see Grant Skinner’s blog post about unloadAndStop() for detailed information). While that’s helpful for SWFs that you don’t control, I think it’s still very important to clean up your own SWFs as best you can manually. Obviously, you have no other choice if you’re still targeting Flash Player 9.

What sorts of things could cause problems when you’re trying to unload a SWF? Running Timer instances, enterFrame events, audio or video that’s still playing or streaming, MovieClip instances that are playing, and content loaders that have not completed yet are all good examples (see Grant’s post above for a longer list). Some of these activities don’t actually keep your SWF in memory, but it’s important to remember that your SWF may not be garbage collected immediately. Code that’s still running for no good reason is simply a waste of resources.

How to clean up after yourself

A SWF can discover when it has been unloaded by listening for Event.UNLOAD on its loaderInfo object. This event handler is the perfect place to do your cleanup. I’ve put together a couple of very simple classes to illustrate how this process works.

First, we have the aptly-named ParentSWF document class for the SWF that loads our second SWF as a child. It’s very simple. It instantiates a Loader to load ChildSWF.swf. When that Loader has completed, it has some code to unload ChildSWF.swf right away:

package
{
	import flash.display.Loader;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.net.URLRequest;

	/**
	 * This is the document class for a parent SWF that loads, then unloads, a
	 * child SWF. It is part of a demonstration of how child SWFs can clean
	 * up after themselves up when they are unloaded.
	 *
	 * @author Josh Tynjala (joshblog.net)
	 */
	public class ParentSWF extends Sprite
	{
		public function ParentSWF()
		{
			super();

			//this loader will load the child SWF. Upon completion, it will
			//immediately unload the child SWF.
			this._loader = new Loader();
			this._loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderCompleteHandler);
			this._loader.load(new URLRequest("ChildSWF.swf"));
			this.addChild(this._loader);
		}

		private var _loader:Loader;

		private function loaderCompleteHandler(event:Event):void
		{
			this._loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loaderCompleteHandler);
			this.removeChild(this._loader);

			//this is where we unload the child SWF
			this._loader.unload();
			this._loader = null;

			//at this point, the child SWF should be ready to be garbage
			//collected. only the child SWF can keep itself in memory now.
			//it had better clean up after itself!
		}
	}
}

Next, let’s take a look at ChildSWF, the document class for our second SWF that is loaded into the first SWF. It includes a running Timer that should be stopped when the child SWF is unloaded. We listen for Event.UNLOAD on loaderInfo to know when the parent SWF has unloaded the child SWF:

package
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.utils.Timer;

	/**
	 * This is the document class for a child SWF is loaded in a parent SWF, and
	 * then unloaded. It is part of a demonstration of how child SWFs can clean
	 * up after themselves up when they are unloaded.
	 *
	 * @author Josh Tynjala (joshblog.net)
	 */
	public class ChildSWF extends Sprite
	{
		public function ChildSWF()
		{
			super();

			//we want to listen to Event.UNLOAD so that we know when the Loader
			//in the parent SWF has unloaded this SWF
			this.loaderInfo.addEventListener(Event.UNLOAD, unloadHandler);

			//this timer, if still running, will keep this SWF in memory after
			//ths SWF is unloaded.
			this._timer = new Timer(1000);
			this._timer.start();
		}

		private var _timer:Timer;

		private function unloadHandler(event:Event):void
		{
			//stop the Timer or this SWF won't be garbage collected
			this._timer.stop();

			//of course, make sure to remove this event listener too!
			this.loaderInfo.removeEventListener(Event.UNLOAD, unloadHandler);
		}
	}
}

To be perfectly clear, the most important parts of the child SWF code above can be summarized in the snippet below:

//listen for the unload event to know when all activities in this SWF should be stopped
this.loaderInfo.addEventListener(Event.UNLOAD, unloadHandler);

function unloadHandler(event:Event):void
{
	this.loaderInfo.removeEventListener(Event.UNLOAD, unloadHandler);

	//DO YOUR CLEANUP HERE!
}

Again, let me cover some of the most common things that should be cleaned up. Be sure to stop audio and video that is playing, MovieClip instances that no longer need to be playing, Timer instances, tweens (they’re probably run by Timer instances or enterFrame events), active listeners for any type of Event.ENTER_FRAME, listeners for events coming from the stage (also a good place to use a weak reference), and be sure to stop any data that may be loading from external sources using URLLoader, Socket, Loader, and other classes of that type. If your child SWF has it’s own child SWFs, be sure to unload those too!

That’s a lot of stuff to remember! Yes, it is, and it’s your responsibility as the developer to ensure that nothing in your SWF causes a memory leak, just like it’s your responsibility to do the same for your individual classes. If you’ve designed the rest of your application with good memory management, then it might not be as bad as you think. Often, you only need to take care of objects that are referenced in the document class. Those objects should already be doing their own work to cleanup any references and event listeners that aren’t needed anymore. For more information on that subject, check out the excellent series of blog posts about AS3 memory management by (once again) the very knowledgeable Grant Skinner.

About Josh Tynjala

Josh Tynjala is a frontend developer, open source contributor, bowler hat enthusiast, and karaoke addict. You might be familiar with his project, Feathers UI, an open source user interface library for Starling Framework that is included in the Adobe Gaming SDK.

Discussion

  1. Auntie

    It really make lots of trouble if the swf (which is going to be loaded by another swf) does not implements the Event.UNLOAD listener!!

    Thanks for the info!!