Умный EventDispatcher…

Как иногда бывает трудно отследить какие слушатели были нацеплены на какой нибудь DisplayObject… А ведь это надо знать, чтобы можно было очистить все слушатели, дабы предотвратить memory-leak и дать возможность сборщику убрать весь мусор. Но к сожалению в классе EventDispatcher не предусмотрен механизм удаления всех своих слушателей и доступа к слушателям тоже нету. Есть массив listeners где хранятся все слушатели, но он приватный. И похоже следующих версия flash player разрабы не собираются это исправлять (хотя им об этом писали).
Как быть? Надо писать свой, хоть и не большой, велосипед…

Давайте назовем его «Умный EventDispatcher«.
Принцип очень простой. В любом классе, который наследует EventDispatcher, переопределить метод добавления слушателей, плюс добавить новый метод для удаления всех слушателей. Когда будет добавляться слушатель он будет сохранятся в ассоциативный массив (который как ключ содержит имя события, а значения будет массивы с присоединенными слушателями) тем самым мы будем всегда знать сколько и каких слушателей у нас есть для данного объекта.
Как я и говорил все очень просто. В этом случае мы всегда полностью удаляем слушатели у объекта тем самым предоставляя его сборщику мусора.

Ниже приведен код всего вышеизложенного (В своих проектах я просто вставляю этот небольшой код в самом низу класса и забываю о проблеме забытых слушателей :) ):
[cc lang="actionscript3"]
///// Имплементация Умного EventDispatcher

/**
*/
private var _listeners:Array = [];

/**
*/
override public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void
{
addListener(type, listener);
super.addEventListener(type, listener, useCapture, priority, useWeakReference);
}

/**
*/
private function addListener(type:String, listener:Function):void
{
if (_listeners[type] == null || _listeners[type] == undefined)
{
_listeners[type] = [listener];
}
else
{
_listeners[type].push(listener);
}
}

/**
*/
public function removeAllListeners():void
{
var arrayOfListeners:Array;
for (var type:String in _listeners)
{
arrayOfListeners = _listeners[type];

for each (var listener:Function in arrayOfListeners)
{
removeEventListener(type, listener);
}

_listeners[type] = null;
}
}

/////

[/cc]

Поделиться в соц. сетях

Опубликовать в LiveJournal
Опубликовать в Google Plus
  • deep

    Костыль костылю рознь. Стоит вместо Array использовать Object чтобы хранить текстовые ссылки. Т.е. _listeners:Object а внутри уже массивы.
    Плюс твой костыль требует повторного использования этого класса в каждом диспатчере где это надо, лучше реализовать это в виде отдельного класса, который с помощью композиции можно было бы легко подключить к любому диспатчеру.
    Или сделать вообще синглетон с подобным функционалом, с помощью которого, можно было бы подписывать любые объекты, даже созданные не тобой (аля SmartDispatcher.addEventListener(target:IEventDispatcher, type:String, listener:Function…)) и т.д.

  • VirtualMaestro

    Ты прав что костыль костылю рознь и в принципе каждый как хочет так и реализовывает, я привел просто идею. Ну а на счет другого:
    1. Стараюсь не использовать Object так как мое убеждение — подобные типы приводят к хаосу и неразберихе в коде, а также очень затрудняет чтение кода тебе и тем кто будет читать за тобой. Я люблю порядок и всегда где возможно использую строгие типы. Хотя такой вариант тоже имеет место быть.

    2. Можно использовать композицию, но в этом случае ты все-равно привносишь third-part код в свой класс. В моем случае я использую именно так для того, чтобы инкапсулировать этот хак — чтобы было «прозрачно» использовать — так как будто ты работаешь с обычным EventDispatcher, тем более что кода совсем мало. А в случае с отдельно взятым проектом мы всегда делаем иерархию классов и достаточно один раз прописать этот код в базовые классы и все.

    Есть такая мысль, чтобы сделать небольшой фреймворк, где были бы переопределены основные классы связанные с EventDispatcher — MovieClip, Sprite, EventDispatcher… Тогда можно было бы имплементировать в них не только этот хак, а и другие хаки, которые бы упростили жизнь :)

    А еще лучше если бы эти вопросы были решены в Adobe, тогда и нам бы гемора было меньше…

  • claymore

    Можно же в addEventListener последним параметром передавать true, тогда при слабом связывании ГК при сборке мусора не обращает внимания на слушатели. Только с асинхронными вызовами нужно быть аккуратнее для локальных объектов, чтобы раньше времени экземпляр не собрали.

  • VirtualMaestro

    Да, можно сделать слабые ссылки, но я и грош не поставлю на то, что это сработает так как от него ожидают. Когда то я тоже этим заинтересовался и начал плотно копать. Прочитав много всяких обсуждений этой темы (это поднималось и в ruFlash и на flashere) я сделал для себя один вывод — этим пользоваться не буду.
    Это может быть на столько не предсказуемая вещь и из-за нее могут быть такие геморои, что легче какой нить фреймвормк написать чем использовать эту шнягу.
    На своем опыте. Делал менеджер звуков и взял какую то библиотеку (уже не помню какую именно), которая предоставляла удобный врапер для работы с Sound. Так вот, вроде все работает норм, вроде трек кончается и должно произойти событие… а оно не происходит… Я чуть с ума не сошел пока изучив код библиотеки понял, что это происходит из-за weak references.
    Мое имхо такое — если ты хочешь четко знать что где создается и что где удаляется и это дело контролировать — не надо полагаться на слабые связи и подобные неконтролируемые вещи, лучше писать свой механизм для этого.

  • claymore

    Я думаю, это все равно, как минимум, стоит упоминания в статье об отписывании слушателей и сборке мусора. Я обычно вообще стараюсь не использовать слушателей, вешаю на стейдж энтерфрейм и мышь с клавой и все. Единственное где необходимы события это на асинхронных вызовах — звук, лоадеры, сеть, но там нормально справляется слабое связывание, главное там, как я выше писал, контролировать ссылки на объекты, чтобы их не собрали раньше диспатча асинхронного события.

    Ну, а на счет контроля … пути ГК неисповедимы))) На всякие хаки с форсированием сборки мусора, думаю тоже не стоит полагаться, потому что они спокойно могут быть залечены в следующих версиях плеера.