Что поддерживает объекты в локальной области при регистрации слушателей? - PullRequest
8 голосов
/ 05 июля 2011

Я вижу этот вид кажущейся магии во всех видах кода AS3, но вот сокращенный пример:

package {
    import flash.display.Sprite;
    import flash.events.*;
    import flash.net.*;

    public class URLLoaderExample extends Sprite {
        public function URLLoaderExample() {
            var loader:URLLoader = new URLLoader();
            loader.addEventListener(Event.COMPLETE, onComplete);
            loader.load(new URLRequest("example.txt");
        } // 'loader' should fall out of scope here!

        private function onComplete(evt:Event):void {
            var loader:URLLoader = URLLoader(evt.target);
            trace ("Received data: " + loader.data);
            //unsure if removal below is necessary (since I don't
            //know where 'loader' itself is hiding!)...
                //  - NOTE: this removal is never in the examples!
            loader.removeEventListener(Event.COMPLETE, onComplete);
        }
    }
}

Как указано в комментарии к коду, переменная loader должна выпасть из области видимости.после конструктора URLLoaderExample.Тем не менее ... кажется, что он все еще остается живым (а не сборщиком мусора) где-то, так как слушатель / обработчик onComplete может получить его без ошибок.на loader, который поддерживает его, чтобы он мог завершить операцию загрузки и затем передать слушателю / обратному вызову onComplete?Можно ли где-нибудь увидеть эту ссылку?

Чтобы помочь с контекстом ... в качестве аналогичного примера я знаю, что для экземпляра loader будет зарегистрирован прослушиватель onComplete.Я также знаю, что мне нужно быть осторожным, чтобы постоянно использовать removeEventListener (?), Чтобы избежать возможных утечек памяти в результате неактивных слушателей.Что меня беспокоит, так это то, что я не понимаю, где находится магическая ссылка loader и нужно ли (или когда) мне это очищать.

Может быть, loader.load() сам вызов вызывает вещи loader где-то глобально?

Ответы [ 4 ]

5 голосов
/ 05 июля 2011

Этот пример определенно подвержен ошибкам, поскольку загрузчик может получить сборщик мусора до завершения загрузки.Когда вы подписываетесь на событие COMPLETE методом onComplete, вы создаете ссылку из загрузчика на ваш класс URLLoaderExample.И то, что вам нужно, чтобы убедиться, что GC не разрушит загрузчик, создает ссылку от до .

GC никогда не гарантирует вам своевременную очистку, даже если вы явно уничтожаете все ссылки.( См. Этот пост для ресурсов по логике GC. ) Но он может собрать мусор в загрузчике в процессе, если на него нет явных ссылок.Если вы попробуете свой тест в приложении, которое использует память (а не просто сидит и ничего не делает), вы, скорее всего, увидите это поведение.И вы, скорее всего, увидите сборщик мусора, если попытаетесь загрузить swfs вместо данных.

Использование слабых ссылок здесь не поможет, потому что когда вы делаете это, вы говорите GC: «не стесняйтесь убивать»На что я, диспетчер, ссылаюсь, мне не жаль этого ».В вашем примере это будет выглядеть так: «не стесняйтесь убивать экземпляр URLLoaderExample, если он потеряет другие жизнеспособные ссылки», что, ну, в общем, бессмысленно. Вот одна хорошая статья об использованииWeakReference.

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

Итак, если коротко ответить на ваш вопрос: ссылка никуда, вам просто повезло увидетьзагрузка работает правильно.Ну, если быть точным, это объект активации функции (как он называется в спецификации ECMA), который используется как область видимости для локальных переменных и ссылается на них.Но в любом случае он удаляется при возврате метода, и вы никогда не сможете получить ссылку на сам объект активации (снова по спецификации).

EDIT Еще несколько слов о том, кто кого удерживает от мусора-collected.Добавлено из-за явного недопонимания в комментариях.

Цитата от Adobe aliveocs :

useWeakReference: Boolean (default = false) - Определяет, является ли ссылка наслушатель сильный или слабый.Сильная ссылка (по умолчанию) не позволяет вашему слушателю собирать мусор.Слабой ссылки нет.

Итак, подписка на событие создает ссылку ОТ диспетчера TO на слушателя.И диспетчер свободен, в отличие от слушателя.Слушатели не мешают диспетчеру собирать мусор.А диспетчер МОЖЕТ помешать слушателям собирать мусор, поэтому у нас useWeakReference.

1 голос
/ 11 июля 2012

Ну, чувак, если ваш вопрос был о том, почему загрузчик находится вне области видимости, вот ваш ответ:

На самом деле, загрузчик, который вы видите в методе конструктора классов (CCM), не тот, который высм. в onComplete: LINE14: var loader:URLLoader = URLLoader(evt.target);

(я не знаю, почему людям нравится ставить одно и то же имя в переменные, это может сбивать с толку, но сейчас я объясню это не так)

Магия заключается в evt.target.Но вы должны спросить себя: "Что делает .target?"Ну, это переменная экземпляра, определенная «объектом события», и она предоставляет ссылку на «целевой объект».

Если вы не знаете, что такое «цель события», прочитайте этот параграф.«Event Target» - это объект, в котором слушатель зарегистрирован на LINE9: loader.addEventListener(Event.COMPLETE, onComplete); Как видите, это загрузчик в CCM (пожалуйста, не путайте имена загрузчиков), который ссылается на new URLLoader();.Заключение параграфа: «Цель события» - это объект URLLoader, на который ссылается локальная переменная загрузки в CCM.

Что ж, используя переменную .target, вы можете иметь ссылку на объект URLLoader, а затем использовать ссылку по своему усмотрению.В этом случае вы использовали ссылку для удаления слушателя.Это нормально.

Вот улучшенная версия (только одна переменная экземпляра, которая предоставляет ссылку на URLLoader):

package {
    import flash.display.Sprite;
    import flash.events.*;
    import flash.net.*;

    public class URLLoaderExample extends Sprite {
        private var lalala:URLLoader;
        public function URLLoaderExample() {
            lalala = new URLLoader();
            lalala.addEventListener(Event.COMPLETE, onComplete);
            lalala.load(new URLRequest("example.txt");
        }

        private function onComplete(evt:Event):void {
            trace ("Received data: " + lalala.data);
            lalala.removeEventListener(Event.COMPLETE, onComplete);
        }
    }
}

Но просто чтобы убедиться, что вас не путают с именами:

package {
    import flash.display.Sprite;
    import flash.events.*;
    import flash.net.*;

    public class URLLoaderExample extends Sprite {
        public function URLLoaderExample() {
            var blabla:URLLoader = new URLLoader();
            blabla.addEventListener(Event.COMPLETE, onComplete);
            blabla.load(new URLRequest("example.txt");
        } // 'loader' fell out of scope here! and it fell look there

        private function onComplete(evt:Event):void {
            var phopho:URLLoader = URLLoader(evt.target);
            trace ("Received data: " + phopho.data);
            loader.removeEventListener(Event.COMPLETE, onComplete);
        }
    }
}

Приветствия ... Если у вас есть какие-либо сомнения относительно операции приведения, используемой в URLLoader(evt.target);, вы можете спросить.

1 голос
/ 05 июля 2011

Когда вы добавляете прослушиватель событий, вы неявно создаете ссылку на объект загрузчика (по умолчанию). Однако вы можете удалить это, установив для Eventlistener «слабую» ссылку.

Вот как вы это сделаете:

loader.addEventListener(Event.COMPLETE, onComplete, false, 0, true);

Последний аргумент устанавливает "useWeakListener" в true, что означает, что ссылка на загрузчик не будет сделана. В этом случае загрузчик должен иметь GC-код.

Важно помнить, что если вы добавляете прослушиватель событий с сильной ссылкой, вам нужно удалить его (как вы делали в примере). Если вы используете слабого слушателя, вам нужно сделать загрузчик частной переменной в классе, иначе ваш обратный вызов находится в гонке с GC.

Вот документация по методу: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/events/IEventDispatcher.html

0 голосов
/ 05 июля 2011

Я не понимаю вашего замешательства. Переменная загрузчика не выводит из области видимости (например, ссылка, на которую вы указывали в памяти, больше не нужна). Однако, поскольку все еще есть ссылка на внешний прослушиватель событий, сам загрузчик не является GC-файлом, поэтому вы можете продолжать получать ссылку на загрузчик (в обработчике он получает цель) и не будут иметь права для GC, пока слушатель события не будет удален.

EDIT: Извините, я должен был быть более ясным по этому вопросу. Я должен был выпить больше кофе. Когда я имею в виду «внешний», я имею в виду такие вещи, как загрузчики, поскольку они зависят от действий браузера. Теперь я не могу сказать это наверняка, так как я никогда не видел базовый код для Flash Player, но я подозреваю, что если у вас есть какие-либо классы типов Loader, которые должны взаимодействовать с браузером, будет ссылка с подчеркиванием ( от игрока до загрузчика), что помешает ему быть GC'ed. Вот почему вы должны всегда выгружать свои загрузчики или около того, это мое понимание Flash Player. Я могу ошибаться, но в прошлом я провел несколько тестов и никогда не видел внешнего слушателя (в данном случае Loaders, но мне интересно, есть ли другие внешние слушатели, которые могут произойти? с воздухом) получить GC, не будучи явным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...