average Joe
average Joe

Reputation: 4605

How to make closure outer scope reference weak. Or any other workaround

Assumption 1. I have utility class called Experiment. It helps listening EnterFrame events on chosen sprites. The API looks following: Experiment.listenEnterFrame(mySprite, onEnterFrameHandler);.

Assumption 2. I want my Experiment.listenEnterFrame class to allow using anonymous handlers. I also want it to be memory-leak resistant (without any cleanUp() method), i.e. reference to mySprite should be weak.

Here's the code:

package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.system.System;
import flash.utils.Dictionary;
import flash.utils.setTimeout;

public class Experiment extends Sprite
{
    public function Experiment()
    {
        new MemoryLeakTest();
        new NoMemoryLeakTest();
        setTimeout(System.gc, 100);
    }

    private static var dict:Dictionary = new Dictionary(true);

    private static function listener(event:Event)
    {
        dict[event.currentTarget]();
    };

    public static function listenEnterFrame(dispatcher:Sprite, callback:Function):void
    {
        dict[dispatcher] = callback;
        dispatcher.addEventListener(Event.ENTER_FRAME, listener, false, 0, true);
    }
}
}

import flash.display.Sprite;

internal class MemoryLeakTest extends Sprite
{
    function MemoryLeakTest():void
    {
        Experiment.listenEnterFrame(this, function () {
            trace("Memory leak");
        });
    }
}

internal class NoMemoryLeakTest
{
    function NoMemoryLeakTest():void
    {
        Experiment.listenEnterFrame(new Sprite(), function () {
            trace("No Memory leak");
        });
    }
}

In above example, the MemoryLeakTest instance is never removed - tracing "Memory leak" forever.

My interpretation of that is that callback closure created in MemoryLeakTest keeps reference to outer scope, which is MemoryLeakTest instance itself. Because of this reference, MemoryLeakTest instance can't be garbage collected.

Do you know any workaround, so I can use anonymous callbacks in my utility method without having to worry about memory freeing?

Upvotes: 1

Views: 297

Answers (1)

Creynders
Creynders

Reputation: 4583

There's a known bug with Dictionaries in AS3. Instead of storing references to functions/methods, references to the closure objects are stored instead.

Which means that if you would like to store a function reference as a key in a Dictionary it will immediately be marked for GC, if the Dictionary uses weak keys, since the Dictionary is referencing the closure object and not the function itself.

This also means that if you try to store a function reference as a value in a Dictionary then the instance the closure object refers to will not be GC'd as long as the Dictionary holds the closure object. (Dictionaries have weak keys not values !) Which is exactly what is happening here.

Upvotes: 1

Related Questions