Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   Система удаления ненужных объектов и слушателей (http://www.flasher.ru/forum/showthread.php?t=204878)

a7s1h1 13.11.2013 14:44

Система удаления ненужных объектов и слушателей
 
Прошу помочь советом.
Дело в том, что у меня, наверное, обоснованное, но всё же параноидальное стремление удалять объекты и слушатели, которые больше не нужны.

Архитекрута моего проекта такова:
Main хранит "уровни" и контролирует переход между ними.
Уровни - это объекты Sprite с соответствующими детьми (надписями, кнопками, объектами и т.д.).
Переход из уровня 1 в уровень 2 представляет собой удаление со сцены уровня 1 и помещение туда уровня 2.
При этом необходимо удалить не только сам уровень 1 со всеми его слушателями, но и всех его детей и внуков с их слушателями.
К сожалению, функции "удалить всю семью со слушателями на всех уровнях ниже вызываемого объекта", откликающуюся на removed_from_stage, я не нашёл (подозреваю, что такой и нет), поэтому делаю так:
При создании любого объекта, которому предстоит иметь детей, я ставлю проверку события removed_from_stage, и в cleanup() удаляю всех его слушателей при помощи removeEventListener, а также всех детей при помощи
Код AS3:

while (this.numChildren > 0) {this.removeChildAt(0) };

.
Соответственно, при удалении уровня 1 удаляются его дети, которые при своём удалении удаляют своих детей и т.д. И все удаляют своих слушателей.
Но при этом получается, что такую "чистилку" приходится приписывать каждому объекту, ведь даже при использовании
Код AS3:

while (this.numChildren > 0) {this.removeChildAt(0) };

удаляются только дети, а "внуки" либо остаются, либо от потери родителя начинают выдавать ошибки.

Прошу прощения за корявое объяснение, но по-другому не смог(
В книгах написано, что ненужные слушатели и объекты надо удалять.
Но неужели для этого надо каждому объекту добавлять слушателя removed_from_stage, чтобы он "почистился" сам и заставил "почиститься" своих детей?

По большому счёту вопрос заключается в том, как это делать правильно, т.е. "как лучше чистить детей и слушателей"?

КорДум 13.11.2013 14:53

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

Tails 13.11.2013 15:03

КорДум,
Таймер и дисплей объекты являются наследниками EventDispatcher. Соответственно, ссылки подписчиков они хранят сами в себе.

Дисплей объект, находящийся на сцене, перед отрисовкой получает событие enterFrame. Это происходит, когда внешний код проверяет, есть ли у него подписки и высылает событие. В случае удаления объекта со сцены, про его подписчиков никто не знает, кроме самого объекта, а значит, удалять его подписчиков не обязательно.

С таймером похожая ситуация, удалять подписчиков не обязательно. Важно - останавливать таймер для удаления.

КорДум 13.11.2013 15:07

Tails,
Не понял. Подпишите объект на enterFrame и удалите ссылку на него. Его хэндлер будет срабатывать так же каждый кадр. Вызовите GC и посмотрите, что хэндлер больше не срабатывает. То же самое с событием тика таймера.

a7s1h1 13.11.2013 15:11

правильно ли я понимаю, что если при удалении "главного родителя" удалить всех его детей, и следующей же строкой запустить
Код AS3:

System.gc()

, то все "потомки" этого "главного родителя" вместе со слушателями будут уничтожены?

Tails 13.11.2013 15:16

Цитата:

Сообщение от КорДум (Сообщение 1152224)
Tails,
Не понял. Подпишите объект на enterFrame и удалите ссылку на него. Его хэндлер будет срабатывать так же каждый кадр. Вызовите GC и посмотрите, что хэндлер больше не срабатывает. То же самое с событием тика таймера.

Да, всё верно. Потому-что дисплей объект находится на сцене, или в потомке который лежит на сцене. Ссылка на него есть, он не удалится а entarFrame будет вызываться. Я имею ввиду, что если этот объект подписал сам себя на enterFrame, но его удалили со сцены или потомка который на сцене (stage == null) - в этом случае объект удалится, даже не смотря на то, что он сам себя подписал на enterFrame.

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

КорДум 13.11.2013 15:18

Tails, ок, я думал, что чего-то не понимаю. Я говорил про объект, который подписан на событие таймера, не про сам таймер. На самом деле мы недопоняли друг друга =)

Добавлено через 8 минут
a7s1h1,
Цитата:

правильно ли я понимаю, что если при удалении "главного родителя" удалить всех его детей...
да, только удалять детей не обязательно. Если на них ссылок из других мест, то они удалятся сами автоматически.

Tails 13.11.2013 15:31

Главное, что мы поняли друг-друга :)

a7s1h1,
Удалять внуков не обязательно, если вы удаляете их родителя, в котором они лежат. Главное, что-бы на этих внуков ещё кто-то не ссылался, кроме их родителя. КорДум вам уже это посоветовал.

Удалять подписчиков можно и нужно, особенно если вы не на 100% понимаете весь замут с этими диспетчерами. Вы так-же можете использовать при подписке - "слабые ссылки", которые не препятствуют удалению объекта, если на него больше никто другой не ссылается.

System.gc()
Работает только в дебаговом режиме, (нужно для отладки). В релизной версий, вы никак не можете самостоятельно вызвать или повлиять на gc.

Ваша главная задача для удаления - не оставлять ссылок, исключением могут быть только "слабые ссылки".

a7s1h1 13.11.2013 15:51

я, возможно, не так понял, но у меня при удалении "главным родителем" всех своих детей "внуки" никуда не пропадают, толькое если их не уничтожит сам родитель. Другими словами,
Объект 1 - родитель для объекта 2
Объект 2 - родитель для объекта 3
При удалении объектом 1 всех своих детей объект 3 по-прежнему остаётся. А при повторном создании объекта 2 у него вместо одного ребёнка становится уже 2 (проверял через trace(numChildren))

Причём, я проверил, на некоторые их неудалённых детей больше никто не ссылался.

Слабые ссылки я ещё не пробовал, но теперь это уже не главная проблема.
Если я не смогу вызывать System.gc() в релизе, значит на него вообще не имеет смысла расчитывать в плане чистки в нужные мне моменты?

Я совсем запутался. Поможет ли для чистки такой подход:
В каждом классе, подлежащем помещению на сцену, писать:

Код AS3:

package {
        import flash.display.Sprite;
        import flash.events.Event;
 
        public class ClassName extends Sprite {
 
                // свойства
                child:Object; // какой-то объект-ребёнок
 
                // Контсруктор
                public function ClassName() {
                        addEventListener(Event.ADDED_TO_STAGE, init);
                }
 
                private function init(e:Event):void {
                        removeEventListener(Event.ADDED_TO_STAGE, init);
                        addEventListener(Event.REMOVED_TO_STAGE, cleanup);
                        // добавляем детей
                        child = new Child();
                        addChild(child);
                        // добавляем слушателей детям (при необходимости)
                        child.addEventListener(Event.ENTER_FRAME,childupdate)
                        // добавляем слушателей себе
                        addEventListener(Event.ENTER_FRAME,update)
                }
 
                private function cleanup(e:Event):void {
                        // удаляем слушателей детей
                        child.removeEventListener(Event.ENTER_FRAME,update)
                        // удаляем детей
                        while (numChildren > 0) { removeChildAt(0) };
                        // удаляем своих слушателей
                        removeEventListener(Event.ENTER_FRAME,update)
                        removeEventListener(Event.REMOVED_TO_STAGE, cleanup);
                }
 
                private function update():void {
                        //
                };
 
                private function childupdate():void {
                        //
                };
        }
}


Tails 13.11.2013 16:22

Создайте в родительском конструкторе столько детей, сколько вам нужно.
Добавьте их в родителя:
Код AS3:

addChild(child);

Подпишитесь в конструкторе на обновление, если вам необходимо:
Код AS3:

addEventListener(Event.ENTER_FRAME,update)

Тут переудалять детей или переотписываться от события нет необходимости, так-как все ссылки содержит родительский класс ClassName.

Если кто-то создать ClassName и добавит его на сцену, он создаст своих детей и будет обновляться. Если ClassName кто-то удалит, GC автоматически заберёт его и всё его содержимое, так-как кроме ClassName, о нём больше никто и ничего не знает.


Часовой пояс GMT +4, время: 04:08.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.