ActionScript - 强制垃圾收集在ADL中不起作用?

The*_*978 1 air garbage-collection actionscript-3 addeventlistener

在ADL中启动以下代码时,为什么方块继续旋转?

var square:Sprite = new Sprite();
square.graphics.beginFill(0xFF0000);
square.graphics.drawRect(-25, -25, 50, 50);
square.x = square.y = 100;
addChild(square);

addEventListener(Event.ENTER_FRAME, rotateSquare, false, 0, true);

function rotateSquare(evt:Event):void
    {
    square.rotation += 2;
    }

System.gc();
Run Code Online (Sandbox Code Playgroud)

更新

以下显示对象具有弱引用的ENTER_FRAME事件侦听器.但是,打电话:

removeChild(testInstance);
testInstance = null;
Run Code Online (Sandbox Code Playgroud)

不会停止ENTER_FRAME事件:

package
{
import flash.display.Sprite;
import flash.events.Event;

public class Test extends Sprite
    {       
    private var square:Sprite;

    public function Test()
        {
        addEventListener(Event.ADDED_TO_STAGE, init);
        }

    private function init(evt:Event):void
        {
        removeEventListener(Event.ADDED_TO_STAGE, init);

        square = new Sprite();
        square.graphics.beginFill(0xFF0000);
        square.graphics.drawRect(-25, -25, 50, 50);
        square.x = square.y = 100;
        addChild(square);

        addEventListener(Event.ENTER_FRAME, rotateSquare, false, 0, true);

//      //Current Solution - only works on display objects
//      addEventListener(Event.REMOVED_FROM_STAGE, removeHandler);
        }

    private function rotateSquare(evt:Event):void
        {
        trace("square is rotating");
        square.rotation += 2;
        }

//  private function removeHandler(evt:Event):void
//      {
//      removeEventListener(Event.REMOVED_FROM_STAGE, removeHandler);
//      removeEventListener(Event.ENTER_FRAME, rotateSquare);
//      }
    }
}
Run Code Online (Sandbox Code Playgroud)

我添加了REMOVED_FROM_STAGE事件侦听器,但这只适用于显示对象.

这是ENTER_FRAME事件特有的问题吗?

Jua*_*ano 5

关于您的更新,我认为您误解了GC的工作原理.基本想法很简单.

创建对象时,flash会在称为堆的存储中分配一些内存.返回对此对象的引用.此引用是您存储在变量中的内容.什么是参考?一种访问此对象的方法.将其视为对象的链接.

var foo:Bar = new Bar();
Run Code Online (Sandbox Code Playgroud)

现在,在某些语言中,在某些时候你必须释放为这个对象分配的内存,或者你有内存泄漏.

在GC环境中,这是自动完成的.当然,你需要一些规则.此规则因具体GC而异,但一般而言,您可以说GC确定如果对象不再可访问,则该对象是可收集的.这是有道理的,因为如果你无法到达某个对象,就无法使用它.你丢失了它的链接.所以,它被认为是垃圾,最终将被收集.

关于如何确定可达性的细节有所不同,但在闪存中它是参考计数和标记和扫描算法的混合.

(以下只是一个高级概述,细节可能不准确)

一种方法是引用计数:它既简单又快速,但并不适用于所有情况.基本上,每个对象都有一个引用计数.每次将此对象分配给变量(即存储对象的引用)时,引用计数都会递增.每次丢失此引用时(例如,将null清空),此计数将减少.如果计数达到0,则表示该对象无法访问,因此它是可收集的.

这在某些情况下工作正常,但没有其他情况.特别是当有交叉引用时.

var foo1:Bar = new Bar();   //  let's call this object Bar_1
var foo2:Bar = new Bar();   //  let's call this one Bar_2
//  at this point, Bar_1 has reference count of 1 (foo1) and Bar_2 has a reference of 1 (foo2)
foo1.theOtherFoo = foo2;
//  now Bar_2 has a RC of 2: foo2 and foo1.theOtherFoo
foo2.theOtherFoo = foo1;
//  now Bar_1 has a RC of 2: foo1 and foo2.theOtherFoo
foo1 = null;
//  foo1 no longer references Bar_1, so its RC is decremented. 
foo2 = null;
//  foo2 no longer references Bar_2, so its RC is decremented. 
//  but still both Bar_1 and Bar_2 have a RC of 1. 
Run Code Online (Sandbox Code Playgroud)

如您所见,Bar_1和Bar_2的RC均为1,但无法访问.这是引用计数不起作用的情况之一.因为所有意图和目的,两个对象都无法访问,但不会被收集.

这就是为什么有标记/扫描算法的原因.从高层的角度来看,它的作用是遍历对象图,从一些根对象开始并分析其关系以确定对象是否可达.该算法将确定即使Bar_1和Bar_2的RC为1,它们也不可达,因此应被视为垃圾并在某个时刻收集.

事件,听众和调度员的工作方式相同.他们不是一个特例.当你这样做时:

function test():void {
    foo1.addEventListener("someEvent",someHandler);
}

function someHandler(e:Event):void {
}
Run Code Online (Sandbox Code Playgroud)

它和做的一样:

function test():void {
    foo1.someProperty = this;
}
Run Code Online (Sandbox Code Playgroud)

效果是foo1现在有一个引用this.您通常在完成后调用removeEventListener有两个原因:

1)你不再想要foo1参考this.2)你不再想要听"someEvent"事件了.

很多人坚持使用弱引用,因为他们认为那时你可以假装你不必打电话removeEventListener(这显然太难......).这是错的.removeEventListener做两件事,两件都很重要.如果您想停止接收某些事件的通知,您必须告诉调度员.这真的很简单.在我看来,在大多数情况下,弱引用是不必要的.有些人主张默认使用它们; 但根据我的经验,在实践中这对他们来说是一项糟糕的服务,因为它让人们更加困惑,鼓励他们编写草率的代码并给他们一种印象,你可以忽略这种语言的基本特征(这并不难拍手)工作.

现在,经过这个相当长的(但希望有建设性的)咆哮,让我们来看看你的代码:

你的精灵不会被收集,因为它有2个参考:

1)square变量2)阶段.

第一个遵循上面的规则.第二个也是如此,但初看起来可能并不那么明显.但是如果你想一下它就有意义了.当你这样做:

addChild(square);
Run Code Online (Sandbox Code Playgroud)

square被添加到Test实例中,然后又添加到实例中stage.将stage永远是活的,所以如果事情可以从到达stage,它的到达.只要square添加了遗骸stage,您就可以确定它不会被收集.

所以,如果你在某些时候做了Sean Thayne建议的话:

removeChild(square)
square = null;
Run Code Online (Sandbox Code Playgroud)

你的Sprite收藏.这并不影响你告诉你的Test对象你想在每次输入框架时被调用的事实.而这正是发生的事情.在你不告诉它之前你不想再接收这个事件(调用removeEventListener),它会给你回电.