ActionScript - принудительная сборка мусора не работает в ADL? - PullRequest
1 голос
/ 17 ноября 2010

при запуске следующего кода в 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?

Ответы [ 2 ]

5 голосов
/ 17 ноября 2010

Что касается вашего обновления, я думаю, вы неправильно понимаете, как работает GC.Основная идея довольно проста.

Когда вы создаете объект, flash выделяет некоторую память в хранилище, которое называется кучей.Ссылка на этот объект возвращается.Эта ссылка - то, что вы храните в переменной.Что такое ссылка?Средство для доступа к этому объекту.Думайте об этом как о ссылке на объект.

var foo:Bar = new Bar();

Теперь в некоторых языках в какой-то момент вы должны освободить память, выделенную для этого объекта, когда вы закончите с ним, или у вас есть памятьутечка.

В среде ГХ это делается автоматически.Конечно, вам нужны некоторые правила.Эти правила варьируются в зависимости от конкретного GC, но в общих чертах можно сказать, что GC определяет, что объект является коллекционным, если он более недоступен.Это имеет смысл, потому что если вы не можете добраться до объекта, вы не можете его использовать.Вы потеряли свою ссылку на него.Таким образом, это считается мусором и в конечном итоге будет собрано.

Особенности определения достижимости могут быть разными, но во флэш-памяти это сочетание подсчета ссылок и алгоритма меток и разверток.

(Ниже приведен только общий обзор, подробности могут быть не точными)

Один из методов - это подсчет ссылок: это просто и быстро, но он не работает во всех ситуациях.По сути, каждый объект имеет счетчик ссылок.Каждый раз, когда вы присваиваете этот объект переменной (т. Е. Сохраняете ссылку на объект), счетчик ссылок увеличивается.Каждый раз, когда вы теряете эту ссылку (например, вы обнуляете свою переменную), это число уменьшается.Если счет достигает 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;

ваш Sprite будет коллекционироваться. Это не влияет на тот факт, что вы сообщили своему Test объекту, что хотите вызываться при каждом вводе кадра. И это именно то, что происходит. Пока вы не скажете, что больше не хотите получать это событие (вызывая removeEventListener), оно перезвонит вам.

2 голосов
/ 17 ноября 2010

Сборка мусора Flash очищает только те элементы / объекты / переменные, которые имеют нулевое число ссылок или имеют только слабые ссылки.

Это означает, что вам нужно это сделать. Чтобы он действительно был gc'd.

removeChild(square)
square = null;
System.gc()
...