Be a good SWF citizen, listen for Event.UNLOAD

by Josh Tynjala

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 the Author

Josh Tynjala is an indie game developer, entrepreneur, Flash and Flex mercenary, and bowler hat enthusiast.

Discussion
  1. Another thing I believe to be helpful is to remove all of your event listeners when unloading a swf. While this isn’t the most pressing issue, I know that a few of my headaches in the past have been from not properly removing event listeners. Especially, like you said about removing ENTER_FRAME listeners.

    posted by Craig on 04.24.2009
  2. You don’t need to remove all of them, Craig, but there are some important ones. In addition to ENTER_FRAME, there’s also any event from the stage, Timer events, and any events on objects that may have been passed into the child SWF from the parent SWF.

    posted by Josh Tynjala on 04.24.2009
  3. [...] weird, right? At first, following my own recent advice on how to cleanup an unloading SWF, I started commenting out certain mouse events used by the buttons. My thinking was that maybe [...]

    posted by Combine buttonMode = true and a mouse click to leak memory - Josh Talks Flash on 04.30.2009
  4. When unloading your .SWF, should you also remove any sprites/movieclips and their children and set them to null?

    Or if you remove the .SWF, does it delete those for you?

    Thanks

    posted by Ryan on 05.07.2009
  5. Ryan, you don’t need to manually remove display objects when you unload a SWF.

    Unloading a SWF works in a similar way to removing other display objects. Say you have a button Sprite, and it contains a TextField for the label and a Sprite or something for the background skin. When you remove the button from the display list and set its variable reference to null, you’re aren’t required to remove the label and the skin. If only the button has a reference to those objects, and no references exist for the button itself, then those objects are also marked for garbage collection too.

    posted by Josh Tynjala on 05.07.2009
  6. Josh –

    Thanks for the reply. That’s a life saver. I was really hoping that what you said was the case.

    posted by Ryan on 05.07.2009
  7. Yay! Good info, thanks a bunch! :)

    posted by Hansen on 05.23.2009
  8. I have a super noob question… but I’m desperate to figure this out so here goes… If your parent swf (called “this” right?) immediately unloads the child swf (the _loader) then how is that content available to view? I’ve tried adding container_mc.addChild(this); but I get the error an object cannot be added as a child to one of it’s children. It looks like the loaderCompleteHandler function is called immediately after the child swf has loaded, should I use a mouse click to call that once I’m done interacting with the loaded child swf?

    posted by Donna on 05.27.2009
  9. I have a question that maybe someone could figure it out.
    I have a Main.swf (parent) that calls a Secondary.swf (child), and i was wondering, it’s any possiblity to unload the Main.swf (parent) from the Secondary.swf (child).

    in simple terms, can a child swf unload his parent swf??

    Im desperate!!

    Thank you

    posted by francisco on 06.27.2009
  10. A SWF cannot unload itself (in AS3 at least, AS2 provided a way to do it), let alone ask a parent SWF to unload.

    posted by Josh Tynjala on 06.27.2009
  11. I have loaded a swf lets say at frame 12, but I want to unload and not see it at say frame 14. Here is how i loaded it. Can anyone help me unload it?

    stop();
    
    import com.jeroenwijering.events.*;
    import com.jeroenwijering.player.*;
    import com.jeroenwijering.plugins.*;
    import com.jeroenwijering.utils.Configger;
    import flash.display.MovieClip;
    import flash.events.Event;
    import flash.external.ExternalInterface;
    import flash.net.URLRequest;
    
    var view:Object;
    
    var videoPlayer = new Loader();
    videoPlayer.contentLoaderInfo.addEventListener(Event.COMPLETE, videoLoaded);
    videoPlayer.load(new URLRequest('DemoPlayer/player-licensed.swf?volume=100&image=image.jpg&icons=false'));
    
    function videoLoaded( evt:Event ):void {
    var ply = evt.target.content;
    ply.addEventListener('READY',playerReady);
    
    // customize flashvars via the ply.config object
    ply.config.resizing = false; // leave this for custom width, height, x, and y
    ply.config.file = 'my.mp3';
    ply.config.height = 295;
    ply.config.width = 622;
    ply.config.skin = 'DemoPlayer/simple2.swf';
    
    // position player on the stage
    videoPlayer.x = 1;
    videoPlayer.y = 19;
    
    addChild(videoPlayer);
    }
    
    function playerReady(evt:Event=null) {
    view = evt.target.view;
    view.sendEvent(ViewEvent.PLAY);
    view.sendEvent(ViewEvent.LOAD,evt.target.config);
    };

    Thanks in advance.

    posted by Jim on 07.07.2009
  12. thanks for the information, I was googling ways to watch the memory usage of objects in flash and came across this. I will definitely implement this in my future projects.
    Expect a decent traffic boost soon, I added it to stumbleupon.

    posted by Hitmen on 07.13.2009
  13. Hey, Thanks a Lot for the Info!

    I am currently working on my portfolio and it came in really useful! thanks again.

    Greetings

    posted by Danilo Wanner on 08.11.2009
  14. Thanks !

    posted by Anndorian on 10.28.2009
  15. Hey there. I have an embedded video from vimeo and when I go to the other frame sound still plays. As I suppose I have to remove the player. Can you help me, how to do that.

    Theres my code: (besides, I’m new on scripting so it would be nice to have a fully explanation how to do that.

    stop();
    
    import flash.system.Security;
    import flash.net.URLRequest;
    import flash.display.Loader;
    import flash.events.Event;
    import flash.events.ProgressEvent; 
    
    var moogaloop:Sprite = new Sprite(); // the video player
    var player_width:int=900;
    var player_height:int=500;
    var clip_id:int = 7520057;
    
    function startLoad():void
    {
    	Security.allowDomain("bitcast.vimeo.com");
    	var v_loader:Loader = new Loader();
    	var v_request = new URLRequest("http://bitcast.vimeo.com/vimeo/swf/moogaloop.swf?clip_id=" + clip_id + "&server=vimeo.com" + "&width=" + player_width + "&height=" + player_height + "&show_title=0&show_byline=0&show_portrait=0&color=ffffff&fullscreen=1");
    	v_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onCompleteHandler);
    	v_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgressHandler);
    	v_loader.load(v_request);
    }
    
    function onCompleteHandler(e:Event):void
    {
    	// Position the player where you want it
    	moogaloop.x = 50;
    	moogaloop.y = 60;
    	moogaloop.addChild(e.currentTarget.content);
    
    	// Create the mask for moogaloop
    	var v_mask:Sprite = new Sprite();
    	with( v_mask.graphics ) {
    		beginFill(0x000000, 1);
    		drawRect(moogaloop.x, moogaloop.y, player_width, player_height);
    		endFill();
    	}
    
    	addChild(v_mask);
    	moogaloop.mask = v_mask;
    
    	addChild(moogaloop);
    }
    
    function onProgressHandler(e:ProgressEvent):void
    {
    	var percent:Number = e.bytesLoaded / e.bytesTotal;
    	trace(percent);
    }
    
    startLoad();
    posted by fivedots on 12.02.2009
  16. fivedots, you should call unloadAndStop() on the Loader when you want the video to unload and to stop playing. Changing frames in your SWF isn’t enough to make it stop.

    posted by Josh Tynjala on 12.02.2009
  17. very very Essential info.i didn’t realist how essential since i started fixing bugs on integration issues. loading/unloading…
    it does a whole lot of good for every one to ensure they clean up on their side.

    thanks for the info

    posted by aditya on 03.09.2010
  18. Hi there, I have a similar problem like Francisco, let me explain.

    I have a very large project, I have Menu who calls sub-menus (childs), and the sub-menus more sub-sub-menus(more childs), they all loads from swf files, ervery time i go to a submenu I want to clear the old swf and remove it from memory.

    When I start navigating it runs ok, but while i go further in the navigation gets sloppier, here is the script I’m using:

    var unicoLoader=new Loader(); 
    addChild(unicoLoader);
    home_mc.b1_btn.addEventListener(MouseEvent.CLICK, menu1) 
    home_mc.b2_btn .addEventListener(MouseEvent.CLICK, menu2)  

    function menu1(e:Event){ 
    unicoLoader.unloadAndStop();
    removeChild(unicoLoader);
    unicoLoader = null;
    System.gc();
    if (unicoLoader == null){
    var unicoLoader = new Loader;
    addChild(unicoLoader);
    unicoLoader.load(new URLRequest(“entorno.swf”))
    }


    function menu2(e:Event){ 
    unicoLoader.unloadAndStop();
    removeChild(unicoLoader);
    unicoLoader = null;
    System.gc();
    if (unicoLoader == null){
    var unicoLoader = new Loader;
    addChild(unicoLoader);
    unicoLoader.load(new URLRequest(“reactiva.swf”))
    }

    Can anyone help me, I can’t find the solution, it should work, but obvioulsly it doesn’t!!!!, I’ve been stucked from the last 5 days and y need to move on, thanks in advanced.

    posted by Adriana on 05.08.2010
Share Your Thoughts

To display code in comments: <pre>Code here. May be multiline. Format XML with &gt; and &lt; entities.</pre>

Some HTML allowed in comments: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>