代码之家  ›  专栏  ›  技术社区  ›  Chunky Chunk

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

  •  1
  • Chunky Chunk  · 技术社区  · 14 年前

    在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();
    

    更新

    以下显示对象具有弱引用的ENTER_FRAME事件侦听器。但是,请致电:

    removeChild(testInstance);
    testInstance = null;
    

    不停止输入帧事件:

    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);
    //      }
        }
    }
    

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

    此问题是否特定于输入帧事件?

    2 回复  |  直到 14 年前
        1
  •  2
  •   Sean Thayne    14 年前

    Flash的垃圾收集只清除引用计数为零或引用较弱的元素/对象/变量。

    这意味着你需要这么做。才是真正的gc。

    removeChild(square)
    square = null;
    System.gc()
    
        2
  •  5
  •   Juan Pablo Califano    14 年前

    关于你的更新,我认为你误解了GC的工作原理。基本思想相当简单。

    创建对象时,flash会在名为堆的存储器中分配一些内存。返回对此对象的引用。这个引用就是您存储在变量中的内容。什么是参考?访问此对象的方法。把它看作是与对象的链接。

    var foo:Bar = new Bar();
    

    现在,在某些语言中,在某个时刻,当您完成此对象时,必须释放为其分配的内存,否则会出现内存泄漏。

    在GC环境中,这是自动完成的。当然,你需要一些规则。这些规则根据具体的GC而有所不同,但一般来说,可以说GC确定对象在不再可访问时是可收集的。这是有道理的,因为如果你够不到一个物体,你就不能使用它。你失去了和它的联系。所以,它被认为是垃圾,最终会被收集起来。

    如何确定可达性的细节各不相同,但在flash中,它是参考计数和标记扫描算法的混合。

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

    一种方法是引用计数:它简单快速,但并不适用于所有情况。基本上,每个对象都有一个引用计数。每次将此对象分配给变量(即存储对该对象的引用)时,引用计数都会递增。每次您丢失这个引用(例如,您清空var),这个计数就会递减。如果计数达到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. 
    

    如您所见,Bar 1和Bar 2的RC都为1,但都是不可到达的。这是引用计数不起作用的情况之一。因为无论出于何种目的,这两个对象都是不可访问的,而且不会被收集。

    这就是为什么有标记/扫描算法。从高层次的角度来看,它所做的是遍历对象图,从一些根对象开始,分析其关系以确定对象是否可访问。此算法将确定,即使Bar 1和Bar 2的RC为1,但它们是不可到达的,因此应被视为垃圾,并在某个点进行收集。

    事件、侦听器和分派器的工作方式相同。他们不是特例。当你这样做时:

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

    这和做一样:

    function test():void {
        foo1.someProperty = this;
    }
    

    结果是foo1现在引用了 this . 完成后,通常会调用removeEventListener,原因有二:

    1) 你不再想要了 foo1 引用 . 2) 你不再想监听“someEvent”事件。

    很多人坚持使用弱引用,因为他们认为这样你就可以假装不必打电话给 removeEventListener (这显然太难了……)。这是错误的。 远程侦听器 做两件事都很重要。如果要停止接收某个事件的通知,必须通知调度程序。真的很简单。在我看来,在大多数情况下,弱引用是不必要的。有些人主张默认使用它们;但根据我的经验,在实践中,这对他们来说是一种糟糕的服务,因为它会进一步使人们感到困惑,鼓励他们编写草率的代码,并给他们留下这样的印象:你可以忽略这种语言的基本特性(这并不难理解)是如何工作的。

    现在,在这段相当长的(但希望是建设性的)咆哮之后,让我们看看您的代码:

    不会收集您的精灵,因为它有两个引用:

    1) 那个 square 变量 2) 舞台。

    第一个遵循上述规则大纲。第二个也是,但乍一看可能不那么明显。但如果你想一想就明白了。当你这么做的时候:

    addChild(square);
    

    广场 添加到 Test 实例,它依次添加到 stage . 这个 阶段 总是活着的,所以如果能从 阶段 ,它是可以到达的。只要 广场 仍添加到 阶段 ,您可以确定它不会被收集。

    所以,如果你在某个时候按照肖恩·塞恩的建议去做:

    removeChild(square)
    square = null;
    

    你的 Sprite 可以收藏。这并不影响你告诉你 测试 当输入帧时要调用的对象。这就是发生的事。在您不告诉它您不想再接收此事件(调用removeEventListener)之前,它会给您回电话。