在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;
不会停止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);
// }
}
}
我添加了REMOVED_FROM_STAGE事件侦听器,但这只适用于显示对象。
这是ENTER_FRAME事件特有的问题吗?
答案 0 :(得分:5)
关于您的更新,我认为您误解了GC的工作原理。基本想法很简单。
创建对象时,flash会在称为堆的存储中分配一些内存。返回对此对象的引用。此引用是您存储在变量中的内容。什么是参考?一种访问此对象的方法。把它想象成对象的链接。
var foo:Bar = new Bar();
现在,在某些语言中,在某些时候你必须释放为这个对象分配的内存,或者你有内存泄漏。
在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.
如您所见,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
引用this
。
2)你不再想听“someEvent”事件了。
许多人坚持使用弱引用,因为他们认为你可以假装你不必打电话给removeEventListener
(这显然太难......)。这是错的。 removeEventListener
做了两件事,两件事都很重要。如果您想停止接收某些事件的通知,您必须告诉调度员。这真的很简单。在我看来,在大多数情况下,弱引用是不必要的。有些人主张默认使用它们;但根据我的经验,在实践中这对他们来说是一项糟糕的服务,因为它让人们更加困惑,鼓励他们编写草率的代码并给他们一种印象,你可以忽略这种语言的基本特征(这并不难工作。
现在,经过这个相当长的(但希望有建设性的)咆哮,让我们来看看你的代码:
您的精灵不会被收集,因为它有2个引用:
1)square
变量
2)舞台。
第一个遵循上面的规则。第二个也是如此,但初看起来可能并不那么明显。但是如果你想一下它就有意义了。当你这样做时:
addChild(square);
square
已添加到Test
实例,该实例又添加到stage
。 stage
始终处于活动状态,因此如果可以从stage
获得某些内容,则可以访问square
。只要stage
仍添加到removeChild(square)
square = null;
,您就可以确定它不会被收集。
所以,如果你在某些时候做了Sean Thayne的建议:
Sprite
您的Test
将可收藏。这并不影响你告诉你的{{1}}对象你想在每次输入框架时被调用的事实。而这正是发生的事情。在你不告诉它之前你不想再接收这个事件(调用removeEventListener),它会给你回电。
答案 1 :(得分:2)
Flash的垃圾收集只清除具有零引用计数或只有弱引用的元素/对象/变量。
这意味着你需要这样做。因为它真的是gc'd。
removeChild(square)
square = null;
System.gc()