Polémique envers Loader.unload()
Par Cédric Tabin le mercredi 16.04.2008, 18:00 - ActionScript - Lien permanent
Décidément cette classe nous en fait voir de toutes les couleurs ! Après mes précédents billets sur la méthode Loader.close() et sa synchronisation, en voici un sur la méthode Loader.unload() qui, semblerait-il, est implémentée de manière un peu trop simpliste par rapport à feu MovieClip.unloadMovie()...
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.
Commentaires
Il y a l"astuce" du weak reference lorsque l'on fait l'addEventListener (dernier paramètre a true)
Mais j'ai remarqué que lorsque l'on faisais ça sur un onEnterFrame, d'un objet, même en supprimant tout ses références, le onEnterFrame continue à tourner jusqu'à un certain moment (je pense peut être a un moment critique pour la mémoire ? je ne sais pas ...)
hello,
et si tu forces le garbage avec system.gc(); ???