PDA

Просмотр полной версии : совместное использование Delegate и EventDispatcher


Ромастый
20.12.2006, 18:02
Все знают, что Delegate.create каждый раз возвращает новый объект. И подписавшись раз на событие таким макаром
myObject.addEventListener("myEvent", Delegate.create(myOtherObject, myOtherObjectMethod));

Отписаться от него потом весьма проблематично.

Можно отписаться от него в самом методе в момент вызова через arguments.caller (с) NoxNoctis

А если надо извне, в любой другой момент?
Вот, тут подумалось, что можно просто обойти цепочку:
class DelegateRemover {
public static function removeDelegate(dispatcher, event, obj, func) {
var queue = dispatcher["__q_"+event];
var l:Number = queue.length;
var i:Number;
for (i=0; i<l; i++) {
var o = queue[i];
if (typeof(o) == "function" && o.target == obj && o.func == func) {
queue.splice(i, 1);
return;
}
}
}
}

Скажем есть класс
import mx.utils.Delegate;
//
class MyClass {
var id;
function MyClass(ID) {
id = ID;
}
function addEvent(event, obj) {
obj.addEventListener(event, Delegate.create(this, report));
}
function report(obj) {
trace(id+" "+obj.type);
}
}
В первом кадре пишем:
import mx.events.EventDispatcher;

var mo = new Object();
EventDispatcher.initialize(mo); // Создали вещатель

myC1 = new MyClass("myC1");
myC1.addEvent("onEvent", mo); // Создали экземпляр myClass и подписали его на onEvent

myC2 = new MyClass("myC2");
myC2.addEvent("onEvent", mo); // Создали контрольный экземпляр

mo.dispatchEvent({type:"onEvent"}); // trace myC2 onEvent myC1 onEvent
trace("_____________________________________________");
DelegateRemover.removeDelegate(mo, "onEvent", myC1, myC1.report); // удалили
mo.dispatchEvent({type:"onEvent"}); // trace myC2 onEvent

etc
20.12.2006, 18:29
Что-то мне подсказывает, что при таком подходе ты можешь удалить не тот обработчик…

Ромастый
20.12.2006, 18:32
Что-то мне подсказывает, что при таком подходе ты можешь удалить не тот обработчик…
Есть такой момент... но только в том случае, если создано более одного делегата.
А смысл вешать два и более делегатов на одно событие?

etc
20.12.2006, 18:37
Ну случаев, когда обработчиков больше одного может быть много.
Три варианта:
1) Написать свой EventDispatcher (или найти более правильный, который работает по типу EventDispatcher в AS3, там не требуется делегата)

2) Написать отдельный класс, который будет хранить ссылку на делегированную функцию и по вызову убивать подписку.

3) Хранить ссылку внутри класса (нерационально)

Ромастый
20.12.2006, 18:45
Ну случаев, когда обработчиков больше одного может быть много.
Хм... поясни. Несколько раз подписать одну и ту же функцию на одно событие? Зачем?
Во всех прочих случаях удаляется нужный обработчик.

3) Хранить ссылку внутри класса (нерационально)
Ага, замучался :))

Ромастый
20.12.2006, 20:15
В порядке теста навешал до двух десятков различных - все работает, удаляется только нужный.
ПРоблемы возникают только при многократном:
myObject.addEventListener("onEvent", Delegate.create(MyOtherObject, method));
myObject.addEventListener("onEvent", Delegate.create(MyOtherObject, method));
... и т.д.
Лечится исключительно опять же многократным вызовом:
DelegateRemover.removeDelegate(myObject, "onEvent", MyOtherObject, method);
DelegateRemover.removeDelegate(myObject, "onEvent", MyOtherObject, method);
...
Но повторюсь - не вижу смысла несколько раз вешать один и тот же обработчик на одно и тоже событие...

Так же вроде все нормально работает с макромедийными компонентами :away:

Ромастый
07.02.2007, 16:59
Гыгы.
Удаляются все подписчики-клоны, созданные Delegate'ом, ежели цикл for заменить на for in и убрать нафих выход из метода. :rolleyes:

class DelegateRemover {
public static function removeDelegate(dispatcher, event, obj, func) {
var queue = dispatcher["__q_"+event];
for (var i in queue) {
var o = queue[i];
if (typeof(o) == "function" && o.target == obj && o.func == func) {
queue.splice(i, 1);

}
}
}
}

Ромастый
07.02.2007, 17:00
Стормозил :rolleyes:

etc
07.02.2007, 17:17
Кстати, Нокс предлагал решение удаления типа arguments.caller.removeEventListener ('someEvent',arguments.callee)

Nirth
07.02.2007, 17:43
А чем не подходит вариант которым польюзуются тысячи программистов на C#



var onSomeEventDelegate:Function = Delegate.create(...);

myInstance.addEventDispatcher("someEvent", onSomeEventDelegate);
myInstance.removeEventDispatcher("someEvent", onSomeEventDelegate);

etc
07.02.2007, 17:47
Дык, это и было предложено. Проблема в том, что ссылка локальная.

Nirth
07.02.2007, 17:55
Ну а я и говорю, вынести ссылку из функции в инстанс класса.

Ромастый
07.02.2007, 18:33
Ну а я и говорю, вынести ссылку из функции в инстанс класса.
3) Хранить ссылку внутри класса (нерационально)
:rolleyes:

Nirth
07.02.2007, 18:38
Ну не знаю почему не рационально в C++\C# хранят в классах.

Ромастый
07.02.2007, 18:45
Коли энтих ссылок много - неудобно получается.
Например, ежели ты итерратор делаешь, как в твоем конвеере эффектов.

Nirth
07.02.2007, 18:49
ну я в сишарпе сделал себе класс DelegateStorage. который является HashTable (в AS это Object), и по ключу я получаю нужный делегат

myStorage['but1Click']
и в классе соотствественно храню просто инстанс этой коллекции.

Ромастый
07.02.2007, 18:57
В хорошо спланированном проекте проблем вообще не возникает.
Но иногда бывает нужно быстро сделать что-то, "прибитое гвоздем".
В этом случае DelegateRemover удобно использовать в качестве "веника", для подчистки всякого мусора, созданного Delegate'ом.

Nirth
07.02.2007, 18:58
Я обычно перестраховываюсь просто.

pirrest
19.04.2007, 15:06
В порядке теста навешал до двух десятков различных - все работает, удаляется только нужный.
ПРоблемы возникают только при многократном:

А можно просто в addEventListener добавить проверку, нет ли ранее добавленого такого-же листенера, и если он есть, то не добавлять. Проверка аналогична проверке в removeEventListener

etc
19.04.2007, 15:44
Да можно это все. У меня есть свой EventDispatcher, который третьим параметром в addEventListener принимает область видимости. Соответственно, проблемы нет.