开发者

AS3 passing a function as a parameter creates memory leaks

I have a function that takes another function as a parameter. Something like this :

public function onHits(target : Shape, callback : Function) : void

I use it by passing a member function as a parameter that should be called whenever the passed target hits something. The function is called many times a frame. So it is used by doing :

//code...
CollisionManager.onHits(myShape, onHitCB);
//code...

The on hit function :

public function onHitCB(hitObject : *) : void 
{
    //removed all code to test this problem
}

When I do this, I have a memory leak. I've isolated the problem to that onHits method and have commented out everything else. onHits is an empty method with no code inside it, onHitCB is also empty. If I comment out the call to onHits, there is no memory leak and if I pass null instead of onHitCB there is no memory leak.

So it's clearly when I pass onHitCB as a parameter that's the problem. So I thought it might be because Flash allocates some memory to create the Function pointer and doesn't release it but I call System.gc() every frame in debug mode and the leak is still there. Which would mean that this is either a bug in the SDK or I'm not doing something right.

I have found a weird workaround by keeping a variable that points to the function which I assign in the constructor of my object :

private var func : Function;

public function MyObject() 
{
    func = onHitCB;
}

and this will clear the memory leak even if I still pass onHitCB as the parameter. So that would mean that it's not the "getter" function to obtain the onHitCB but something else causing the memory leak?

I'm very confused. How can this cause a memory leak :

public function MyObject() 
{
}

public function update() : void
{
    CollisionManager.onHits(myShape, onHitCB);//empty function
}

public function onHitCB(hitObject : *) : void 
{
    //removed all code to test this problem
}

but not this? :

private var func : Function;
public function MyObject() 
{
    func = onHitCB;
}

public function update() : void
{
    CollisionManager.onHits(myShape, onHitCB);//empty function
}

public function onHitCB(hitObject : *) : void 
{
    //removed all code to test开发者_JAVA百科 this problem
}

and is there a way to not have to do this workaround?


[...] bound methods are automatically created when you pass a method as a parameter. Bound methods ensure that the this keyword always references the object or class in which a method is defined. Source

That sounds like creating a reference to a method isn't using a simple getter. A new method closure object is generated. So your assumption is right.

I wonder why the references aren't cached for each instance and why they aren't garbage collected though. Better avoid creating multiple references. Referencing a method only once is exactly what I would do when I would have to use that method in multiple places, so most of the time I wouldn't call it a workaround but a good DRY practice. In your example it makes sense of course, assuming a method reference would be using a simple getter.


For more information on exactly what does and does not cause memory leaks when you use functional techniques, check out http://www.developria.com/2010/12/functional-actionscript-part-1.html . Also, be aware that using static methods like this is really bad practice (http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/), and you're just beginning to encounter the many problems that are caused by using this technique. It sounds like you're early enough in your project that you're not completely committed to this path, so you might want to look at other ways to program this.


I'm not sure what your code is about in the onHits function, but if it not requiring additional time to finish in another cycle. then i recomend you to do like this:

static public function onHits(target : Shape) : *
{
    // do what you need

    // return the hitObject;
    return hitObject;
}

and

public function update() : void
{
    // parse the object direc to to the function.
    onHitCB ( CollisionManager.onHits(myShape) );
}

public function onHitCB(hitObject : *) : void 
{
    if ( hitObject == null )
        return;

    // if it not null then do all your calculations.
    //removed all code to test this problem
}


And this is why we don't do this kind of thing in OOP style programing.
Your best bet is to do it properly and add the callback to the CollisionManager class.
The reason it can be GCed when you keep a local reference is because the function never loses scope because that var is there holding the reference.
Once something looses scope it becomes nearly impossible to GC it.

Try this and watch how you lose scope.

private var somevar:String = 'somevar with a string';
public function MyObject() 
{
}

public function update() : void
{
    CollisionManager.onHits(myShape, onHitCB);//empty function
}

public function onHitCB(hitObject : *) : void 
{
    trace(this.somevar) // scope should be lost at this point and somevar should be null or toss an error.
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜