Search code examples
actionscript-3garbage-collectiondisposeevent-listener

How To Dispose Old Sprite Objects With All Event Listener References?


In ActionScript-3 in my level editor I have for each level a new object. Old level objects are removed from stage. So only 1 level object is at stage. I don't keep them in an array or something hoping that garbage collection disposes them for me. My problem seems to be that methods of old objects are still referenced by event listeners that would fire events to the new object and also to the old ones. Old objects are not on stage and therefore invisible but when saving I notice that the old levels have a state caused by editing a newer level.

Now: How can I dispose my objects for sure ?

edit: I also have some event listeners added to stage (KeyDown, MouseMove). Might those few be the problem and others that are just added to the object are removed with the object automatically ?


Solution

  • One approach is to simply setup your event listeners to use weak references by setting the userWeakReference of addEventListener() property to true:

    addEventListener(MouseEvent.CLICK, mouseClickHandler, false, 0, true);
    

    A better pattern is to setup a lifecycle of listeners, such as add listeners on Event.ADDED_TO_STAGE, then remove them on Event.REMOVED_FROM_STAGE

    In your constructor, listen for Event.ADDED_TO_STAGE:

    // listen for added to stage
    addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
    

    When your class is added to stage, apply your listeners and listen for Event.REMOVED_FROM_STAGE:

    protected function addedToStageHandler(event):void
    {
        // remove added to sage handler, listen for removed from stage
        removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
        addEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler);
    
        // setup listeners for class
        addEventListener(MouseEvent.CLICK, mouseClickHandler);
        addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
        addEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
        addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
    }
    

    When your class is removed from stage, remove remaining listeners:

    protected function removedFromStageHandler(event):void
    {
        // remove removed from sage handler
        removeEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler);
    
        // remove listeners for class
        removeEventListener(MouseEvent.CLICK, mouseClickHandler);
        removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
        removeEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
        removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
    }
    

    Even better yet is to implement a dispose() function for your class:

    public function dispose():void
    {
        removeEventListener(MouseEvent.CLICK, mouseClickHandler);
        removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
        removeEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
        removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
    }
    

    Often I'll implement an IDisposable interface to my classes, then dispose as:

    // remove children
    while (numChildren > 0)
    {
        var displayObject:DisplayObject = removeChildAt(0);
    
        if (displayObject is IDisposable)
            IDisposable(displayObject).dispose();
    }