Ce bug a déjà été expliqué dans ce billet par Grant Skinner et fait actuellement l'objet d'un débat animé sur la mailing-list de flashcoders.

Tout d'abord le cas simple d'un fichier SWF qui en charge un autre. Le fichier chargé contient un bouton (clipButton) ainsi que le code suivant :

//loaded.swf
function clickHandler(evt:MouseEvent):void
{
	trace("click");
}
clipButton.addEventListener(MouseEvent.CLICK, clickHandler);

Et dans le fichier chargeur :

//loadManager.swf
var ldr:Loader = new Loader();
addChild(ldr);
 
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
ldr.load(new URLRequest("loaded.swf"));
 
function completeHandler(evt:Event):void
{
	ldr.unload();
}

Rien ne sera affiché, et pour cause, l'appel à unload() vide le contenu du Loader. Si l'on modifie maintenant le code du SWF chargé par celui-ci :

//loaded.swf
function efHandler(evt:Event):void
{
	trace("enterFrame");
}
clipButton.addEventListener(Event.ENTER_FRAME, efHandler);

Suite à la relance du loader, la sortie suivante s'affiche :

enterFrame
enterFrame
enterFrame
enterFrame
enterFrame
...

Qu'est-ce que cela signifie ? Tout simplement que le SWF chargé n'a pas été supprimé et que ses ressources utilisées n'ont pas été libérées ! C'est un comportement fondamentalement différent de MovieClip.unloadMovie() en ActionScript 2 qui avait pour effet de supprimer illico le contenu dudit SWF.

En ActionScript 3, la méthode Loader.unload() se contente uniquement de supprimer la référence de Loader.content vers le contenu en espérant que le Garbage Collector puisse virer le contenu par la suite. Du coup, il suffit qu'une référence soit gardée ailleurs pour que le contenu ne soit jamais supprimé :

//loadManager.swf
var ldr:Loader = new Loader();
addChild(ldr);
 
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
ldr.load(new URLRequest("loaded.swf"));
 
function completeHandler(evt:Event):void
{
	var cnt:MovieClip = (ldr.content as MovieClip);
	ldr.unload();
	trace("cnt : "+cnt);
	trace("ldr.content : "+ldr.content);
}

Ce qui donne :

cnt : [object MainTimeline]
ldr.content : null

Cela amène un certain nombre de problématiques quant à la libération des ressources par le Garbage Collector. Le développeur doit donc faire deux choses pour que le contenu puisse être déchargé :

  • Supprimer toutes les références vers un objet quelconque du fichier chargé.
  • Supprimer tous les écouteurs de l'événement Event.ENTER_FRAME ainsi que stopper tous les Timer

En bref une belle galère si ce souci n'a pas été pris en compte au début... Une petite consolation tout de même, lorsque la méthode Loader.unload() est appelée, un événement de type Event.UNLOAD est envoyé par le LoaderInfo du SWF chargé, ce qui peut permetre de préparer le contenu pour le futur passage du Garbage Collector.