Flash AS3将函数作为参数传递会导致内存泄漏

Flash AS3将函数作为参数传递会导致内存泄漏,flash,actionscript-3,memory-leaks,function-pointers,Flash,Actionscript 3,Memory Leaks,Function Pointers,我有一个函数,它把另一个函数作为参数。大概是这样的: public function onHits(target : Shape, callback : Function) : void 我通过传递一个成员函数作为参数来使用它,只要传递的目标命中某个对象,就应该调用该参数。该函数在一帧内被调用多次。因此,它被用于: //code... CollisionManager.onHits(myShape, onHitCB); //code... 点击功能: public function onHi

我有一个函数,它把另一个函数作为参数。大概是这样的:

public function onHits(target : Shape, callback : Function) : void
我通过传递一个成员函数作为参数来使用它,只要传递的目标命中某个对象,就应该调用该参数。该函数在一帧内被调用多次。因此,它被用于:

//code...
CollisionManager.onHits(myShape, onHitCB);
//code...
点击功能:

public function onHitCB(hitObject : *) : void 
{
    //removed all code to test this problem
}
当我这样做时,我有一个内存泄漏。我已将问题隔离到onHits方法,并已注释掉所有其他内容。onHits是一个空方法,其中没有代码,onHitCB也是空的。如果我注释掉对onHits的调用,则没有内存泄漏,如果我传递null而不是onHitCB,则没有内存泄漏

所以很明显,当我把hitcb作为一个参数传递时,这就是问题所在。所以我认为这可能是因为Flash分配了一些内存来创建函数指针,并且没有释放它,但我在调试模式下每帧调用System.gc(),泄漏仍然存在。这意味着这要么是SDK中的一个bug,要么是我做得不对

我发现了一个奇怪的解决方法,即保留一个变量,该变量指向我在对象的构造函数中指定的函数:

private var func : Function;

public function MyObject() 
{
    func = onHitCB;
}
这将清除内存泄漏,即使我仍然将onHitCB作为参数传递。这意味着获取onHitCB的不是“getter”函数,而是导致内存泄漏的其他原因

我很困惑。这如何导致内存泄漏:

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
}
但不是这个

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 this problem
}

有没有一种方法不必这样做?

我不确定onHits函数中的代码是关于什么的,但是如果它不需要额外的时间在另一个周期中完成的话。那么我建议你这样做:

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

    // return the hitObject;
    return hitObject;
}

[…]绑定方法是在将方法作为参数传递时自动创建的。绑定方法确保this关键字始终引用在其中定义方法的对象或类

这听起来好像创建对方法的引用并不是使用简单的getter。生成了一个新的方法闭包对象。所以你的假设是正确的


我想知道为什么没有为每个实例缓存引用,为什么没有对引用进行垃圾收集。最好避免创建多个引用。当我必须在多个地方使用某个方法时,只引用一次该方法正是我所要做的,所以大多数时候我不会将其称为一种变通方法,而是一种很好的实践。在您的示例中,假设一个方法引用将使用一个简单的getter,这当然是有意义的。

有关在使用函数技术时哪些因素会导致内存泄漏,哪些因素不会导致内存泄漏的详细信息,请查看。另外,请注意,像这样使用静态方法是非常糟糕的做法(http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/),并且您刚刚开始遇到使用此技术导致的许多问题。听起来你在项目中做得够早了,你还没有完全致力于这条道路,所以你可能想看看其他的编程方法。

这就是为什么我们不在OOP风格的编程中做这种事情。
最好的办法是正确地执行此操作,并将回调添加到CollisionManager类中。
保留本地引用时可以对其进行GCed的原因是,函数永远不会因为保存引用的var而失去作用域。
一旦某个东西失去了作用域,就几乎不可能对其进行GC。

试试这个,看看你是如何失去视野的

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.
}

目前,onHits函数什么也不做。我已经删除了它的所有代码,但是通过传递函数指针来调用它会导致内存泄漏。我可以通过更改代码的设计来避免这个问题,但简单地传递函数指针不应该导致这个问题(>。我明白了,这证实了我对每次传递函数时都会创建一个对象的怀疑。但是我仍然不明白为什么在传递函数的同时保留一个本地引用(而不是本地引用)将不再创建内存泄漏。我认为它仅在直接使用方法名时(在赋值时或用作参数时)才会生成.So
func=onHitCB;
将只生成一次方法闭包,使用
func
作为参数不会导致再次生成方法闭包。是的,但我没有使用func作为参数,我仍然使用onHitCB(如果仔细查看上面的两个示例,它们都使用onHitCB调用onHits)这对我来说是最大的秘密。我现在最好的解释是,方法闭包被缓存为弱引用,通常会被垃圾收集器清除,但由于未知的原因不会。因为它是弱引用,所以每次调用都需要重新生成,但因为我在本地保留了一个引用,所以它保持有效e足够长,可以在以后的调用中重用。为什么不让onHitCB成为CollisionManager的类成员?听起来您的函数正在失去作用域。在onHits的最后一行,try callback=null;尝试在onHits结束时将回调设置为null,但泄漏仍然存在。到目前为止,保留对函数的本地引用是唯一的解决方法我能够找到。这不应该工作吗?我不确定失去作用域是什么意思(函数指针保留对它们所属的实例的引用,以便可以使用此参数进行正确调用)。通过参数传递函数可以很好地工作,并像应该的那样跟踪“带字符串的somevar”(我认为?)。我不明白为什么在检测到冲突时传递必须调用的回调函数会是“糟糕的设计”,每个对象都可以而且可能应该以不同的方式处理冲突。这在多种情况下使用(我相信box2D会为其冲突引擎这样做)因为它更灵活。好吧,坏例子。如果我有时间,我会发布一篇编辑文章,让你更好地解释我在说什么。
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.
}