#015 [Q] Tips: Timers responsables

La verdad que vamos hasta arriba de trabajo, pero cada viernes que pasa que no cumplo con el objetivo que me he marcado, me duele, asi que aprovecho cosas que salen en el día a día para tratar de trasladarlas al blog.

En Flash, a día de hoy y muy lamentablemente (y no os quiero contar en Flash Lite) no podemos llamar explícitamente al “garbage collector”, sino que lo que hacemos es tratar de marcar los elementos a borrar de memoria dejandolos libres de referencias (bien con un delete, con un null, etc.) De esta manera, si logramos que no queden referencias sobre los objetos que hemos marcado para el borrado, lograremos que cuando el GC haga su tarea, sean liberados de memoria.

Un punto especialmente delicado en este proceso de borrado de referencias son los listeners, ya que son procesos en los que es fácil que nos olvidemos eliminarlos, aunque seamos muy metódicos en nuestro código. Pues hoy os quiero hablar de un desliz que es bastante común cometer, y que también es bastante sencillo de solucionar: los listeners sobre TimerEvent de un Timer.

En la referencia de AS3 encontramos lo siguiente:

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

public class TimerExample extends Sprite {

public function TimerExample() {
var myTimer:Timer = new Timer(1000, 2);
myTimer.addEventListener("timer", timerHandler);
myTimer.start();
}

public function timerHandler(event:TimerEvent):void {
trace("timerHandler: " + event);
}
}
}

En este ejemplo no tenemos explícitamente un manejador para el evento TimerEvent.TIMER_COMPLETE, con lo que se hace más evidente, que en algún momento deberíamos desuscribir los eventos del Timer cuando no lo queramos usar más. En el código anterior, deberíamos tener de alguna manera la posibilidad de remover el Listener creado, y la verdad que la ayuda de flash en este caso no clarifica demasiado este punto.

Una cosa que hay que tener clara para comprender el problema es que ni aun cuando un TIMER se ha completado, los listeners son liberados, salvo que lo hagamos explícitamente. Para comprobarlo prueba este código en Flash:

package  com.qinteractiva.blog.demos
{
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.display.MovieClip;

public class DemoTimer extends MovieClip
{
private var demo:Timer;
private const LAPSO:int = 2000;
private const ITERACIONES:int = 2;

public function DemoTimer()
{
// constructor code
demo = new Timer(LAPSO, ITERACIONES);
demo.addEventListener(TimerEvent.TIMER, onTimer);
demo.addEventListener(TimerEvent.TIMER_COMPLETE, onComplete);

demo.start();
}

public function onTimer(ev:TimerEvent):void
{
trace("timer!");
}

public function onComplete(ev:TimerEvent):void
{
trace("timer completado!");
trace("listener timer: "+demo.hasEventListener(TimerEvent.TIMER));
trace("listener complete: "+demo.hasEventListener(TimerEvent.TIMER_COMPLETE));
}
}
}

Cuando el Timer se completa, podemos ver que sus listeners siguen ahi, activos, y que el hecho de completarse no hace que se eliminen automáticamente, por tanto tenemos que tener claro que debemos removerlos manualmente si queremos poder marcar ese timer para ser borrado de memoria.
En el caso del ejemplo, es una propiedad de la clase, con lo que tenemos bien clara la referencia para acceder a él.

Aunque el timer sea una variable local de una función, como en el ejemplo de la ayuda de AS3 del inicio del post, debemos eliminarlo, y un buen lugar para hacerlo es en el TIMER_COMPLETE, accediendo al timer desde el evento que recibe el manejador, algo como esto:

public function onComplete(ev:TimerEvent):void
{
var demo:Timer = Timer(ev.target);

demo.removeEventListener(...);
demo.removeEventListener(...);
}

Asi que recuerda, liberad al Timer!!!