Jump to content
Search Community

unloading and cleanup

aaseb test
Moderator Tag

Recommended Posts

Hello, I'm trying to load and unload several swf files using loadermax.

The problem I run into is that when I unload a swf, it stays in the system memory.

 

After a lot of testing I got it working loading a simple swf with no event listeners inside. When I load a more complex swf with a lot of code inside and event listeners, that's when the system memory keeps going up. So like greensock suggested in an earlier post the problem is coming from cleanup code missing. The thing is that cleanup code is really a lot of work. I ran into this same problem without using loadermax and managed to go around it using application domain than had to set the domain and loader to null so I think it should be possible to go around this problem using loadermax too.

Any clues?

Link to comment
Share on other sites

still doing some more testing to see exactly what causes the problem and how to fix it.

having event listeners on buttons in the loaded swf will not cause the problem,

having variables in the loaded swf will not cause the problem

having event listeners on the stage in the loaded swf WILL cause the problem.

 

There's probably other things that cause the problem but maybe not so many so adding cleanup code on unload is probably the best solution. Now I'm trying to figure out how to do that.

i copied this code from another post:

 

var curParent:DisplayObjectContainer = this.parent;
while (curParent) { 
   if (curParent.hasOwnProperty("loader") && curParent.hasOwnProperty("rawContent")) {
       Object(curParent).loader.addEventListener("unload", dispose, false, 0, true); 
   }
   curParent = curParent.parent;
}

function dispose(event:Event):void { 
   //do cleanup stuff here like removing event listeners, stopping sounds, closing NetStreams, etc... 
}

 

i just changed "loader" to correspond to the appropriate SWFloader but it seems like the function is not being called on unload.

Link to comment
Share on other sites

i just changed "loader" to correspond to the appropriate SWFloader but it seems like the function is not being called on unload.

Well don't do that :)

 

Seriously, that's the problem - you shouldn't change "loader" to anything else. That code is looking for the "loader" property for the ContentDisplay instance.

Link to comment
Share on other sites

ok the function is called when i leave the code as it is

now I add my cleanup code like this :

 

function dispose(event:Event):void { 
   //do cleanup stuff here like removing event listeners, stopping sounds, closing NetStreams, etc... 
stage.removeEventListener(Event.RESIZE, stagefunction);
trace("cleanup done");
}

 

and i get this :

TypeError: Error #1009: Cannot access a property or method of a null object reference.

at child1_fla::MainTimeline/dispose()

at flash.events::EventDispatcher/dispatchEventFunction()

at flash.events::EventDispatcher/dispatchEvent()

at com.greensock.loading.core::LoaderCore/_dump()

at com.greensock.loading.core::LoaderItem/_dump()

at com.greensock.loading.core::DisplayObjectLoader/_dump()

at com.greensock.loading::SWFLoader/_dump()

at com.greensock.loading.core::LoaderCore/unload()

at test_fla::MainTimeline/frame61()

at flash.display::MovieClip/gotoAndPlay()

at test_fla::MainTimeline/gotounload()

Link to comment
Share on other sites

I tried that, it seems the stage is null so the cleanup is not happening.

if i put this before the cleanup code :

trace("stage :" + stage); i get : stage :[object Stage]

but if i put it inside the cleanup code i get stage : null.

Link to comment
Share on other sites

Keep in mind that whenever a DisplayObject is removed from the stage, its "stage" property becomes null. So in order for you to properly remove your listeners, you should store a reference to the stage in a variable so that when your dispose() method is called (after the swf was unloaded and removed from the stage), it can remove the event listener(s). You could create that variable as soon as your swf is added to the stage. Kinda like this (in your sub-swf):

 

var _stage:Stage = this.stage;
if (_stage == null) {
   this.addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler, false, 0, true);
}
function addedToStageHandler(event:Event):void {
   this.removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
   _stage = this.stage;
}

function dispose():void {
   if (_stage != null) {
       _stage.removeEventListener(Event.RESIZE, stagefunction);
   }
}

Link to comment
Share on other sites

IT WORKS! I only had to change "addedToStageListener" to "addedToStageHandler"

and "function dispose():void {" to "function dispose(event:Event):void {"

 

you're the man greensock. I still think if there was a way to avoid having to put this cleanup code it would make life more simple.

Is there a list somewhere of things that need to be cleaned up and things that don't.

Link to comment
Share on other sites

I still think if there was a way to avoid having to put this cleanup code it would make life more simple.

Is there a list somewhere of things that need to be cleaned up and things that don't.

Yeah, I wish it were possible for LoaderMax to know all the stuff you do inside your swf and remove any stage event listeners you added, kill your nested NetStreams, etc. but unfortunately it's not. As far as there being a list somewhere, I'm not aware of any but it's generally considered a good practice in ActionScript (and any other language) to clean up after yourself anyway.

Link to comment
Share on other sites

This is the way I had solved this problem without doing any cleanup and before using loadermax.

Thanks to this link :

http://simplistika.com/as3-garbage-collection-solved/

Now the code and sample file they give works fine since it's only an image being loaded. To make it work with a sloppy swf with no cleanup inside I had to add just a few things.

 

Here is the modified code :

 

import flash.display.Loader;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import flash.system.System;
import flash.net.LocalConnection;

var t : Timer = new Timer(2000);
var l : Loader = new Loader();

/* use a loadercontext with a new applicationdomain() */
var lc : LoaderContext = new LoaderContext();
lc.applicationDomain = new ApplicationDomain();

t.addEventListener(TimerEvent.TIMER, fOnTimer);
t.start();
addChild(l);

function fOnTimer(e : TimerEvent) : void
{
if (l.content != null)
{
	l.unloadAndStop();
	//this is what i added
	removeChild(l);
	l = null;
	l = new Loader();
	addChild(l);
	lc = null;
	trace(System.totalMemory)
	//end of what i added
	System.gc();
	System.gc();
	try {
	new LocalConnection().connect('gc');
	new LocalConnection().connect('gc');
	} catch (e:*) {}
}
else
	l.load(new URLRequest("child1.swf"), lc);
}

 

Now this is surely not best practice and it's probably better to do the cleaning yourself but it saves a lot of time and it works. I tried to adapt this code for loadermax but I must be doing it wrong. Could it work with loadermax too?

Link to comment
Share on other sites

This is what I come with when I adapt this code for loadermax :

 

import flash.display.Loader;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import flash.system.System;
import flash.net.LocalConnection;

import com.greensock.*;
import com.greensock.loading.*;
import com.greensock.events.LoaderEvent;
import com.greensock.loading.display.*;

var t : Timer = new Timer(2000);
//var l : Loader = new Loader();
//this was changed to this :
var l:SWFLoader = new SWFLoader("child1.swf", {name:"childClip1", context:lc});

/* use a loadercontext with a new applicationdomain() */
var lc : LoaderContext = new LoaderContext();
lc.applicationDomain = new ApplicationDomain();

t.addEventListener(TimerEvent.TIMER, fOnTimer);
t.start();
//addChild(l);
//this was changed to this :
l.load();
addChild(l.content);

function fOnTimer(e : TimerEvent) : void
{
if (l.content != null)
{
	//l.unloadAndStop();
	//this was changed to this :

	l.unload();
	//removeChild(l);
	//this was changed to this :
	removeChild(l.content);
	trace("l.content is " + l.content)
	l = null;
	//l = new Loader();
	//this was changed to this :
	l = new SWFLoader("child1.swf", {name:"childClip1", context:lc});
	//addChild(l);
	//this was removed
	lc = null;
	trace(System.totalMemory)
	System.gc();
	System.gc();
	try {
	new LocalConnection().connect('gc');
	new LocalConnection().connect('gc');
	} catch (e:*) {}
}
else
	//l.load(new URLRequest("child1.swf"), lc);
	//this was changed to this :
	l = new SWFLoader("child1.swf", {name:"childClip1", context:lc});
	l.load();
	addChild(l.content);
}

 

notice the "trace("l.content is " + l.content)" I added, in this code it returns "l.content is [object ContentDisplay]" as when I test the same thing in the previous code without loadermax, I get "l.content is null". I think this might be why it's not working. Any ideas on how to make the swfloader content null?

Link to comment
Share on other sites

You shouldn't need to worry about all this - have you looked inside SWFLoader, ImageLoader, and the DisplayObjectLoader that they both extend? You'll see that the unload() or unloadAndStop() is called on the Loader instance, new LocalConnection() is called twice on several successive frames to ensure gc kicks in, and stop() is called on all nested MovieClips. One of the primary goals in building LoaderMax was to make this sort of thing automatic.

 

Simply call dispose(true) on your SWFLoader and it'll do all that it can to release stuff for gc.

 

Oh, and by the way, System.gc() is rather pointless - it only works in the debug version of Flash Player.

 

Is there something that made you think that SWFLoader isn't releasing things properly for gc? If so, make sure you're removing any stage event listeners that you added from within your child swf and that you close any NetStreams that are being streamed into the child swf, etc. Basically, clean up after yourself in the child because it would be impossible for SWFLoader/LoaderMax to do that for you.

 

As far as the loader's "content" still returning a valid ContentDisplay object, that's normal. Remember, that's not a reference to the raw swf content that was loaded - it's just a reference to the ContentDisplay Sprite which is a wrapper into which the raw content is loaded, If you don't dispose(true) your loader, it won't null that reference because it needs to keep it around in case you load() the SWFLoader again. See what I mean?

 

Does that help at all?

Link to comment
Share on other sites

Is there something that made you think that SWFLoader isn't releasing things properly for gc??

 

YES! In the example without loadermax the memory load goes up when the child swf is loaded and down every time the child swf is unloaded (no cleanup code needed).

In the example with loadermax, the memory load keeps going up and never comes down.

Even after calling "l.dispose(true);" The trace still returns l.content is [object ContentDisplay].

I know I'm trying to do things the wrong way and proper cleanup is the right way but the way I see it is if it works without loadermax, why can't it work with loadermax.

Link to comment
Share on other sites

Well, after a few hours of hammering away at this, I was finally able to reproduce the problem and figure out a solution. The problem would only show itself if you added non-weak event listeners to the stage from within your subloaded swf and never cleaned them up before dispose(true) was called. The interesting thing that I discovered was that in order for Flash to have the best chance of garbage collecting the sub-swf, the Loader's unloadAndStop() method would need to be called WHILE the Loader was in the display list. So if the loader was removed from the display list (with removeChild()) before unloadAndStop() was called, you'd see the issue. Feel free to test it in your code too with the standard Loader (not LoaderMax). Odd, I know. I can't tell you how many bugs and strange things I've come across in Adobe's stuff as I worked on LoaderMax. This one isn't even the weirdest.

 

The good news is that I altered the order in which things are called inside SWFLoader so that it has the best chance of gc'ing stuff. I re-ran a similar test as yours and it appears to work great now, even with stage event listeners that aren't weakly referenced.

 

As you said, it's always best to clean up after yourself anyway. I wouldn't recommend relying on LoaderMax's built-in routines to avoid it. But I definitely put code in place that will maximize the chances of things being gc'd properly with minimal hassle on your part. Again, you don't need to do any of the funky workarounds in your example - just call dispose(true) and LoaderMax/SWFLoader will take care of it for you.

 

Snag the latest version at

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...