PDA

View Full Version : Event.handled in AS3 - why???



wvxvw
December 13th, 2009, 08:55 AM
Well, attempting to optimize scrollBar performance I've discovered that some event won't dispatch - I went all though that's even life with debugger, and, at some point it was like this:

private const _scrollEvent:ScrollEvent = new ScrollEvent(ScrollEvent.SCROLL);
public override function set scrollRect(value:Rectangle):void
{
super.scrollRect = value;
trace(super.hasEventListener(ScrollEvent.SCROLL)); // true
super.dispatchEvent(this._scrollEvent);
}
in ScrollEvent


public override function clone():Event { return this; }
and the handler:

this._scrollBar.addEventListener(ScrollEvent.SCROL L, this.scrollHandler);
private function scrollHandler(event:Event):void
{
trace(event); // called only once...
}
I never call myself nothing like stopPropagation() or stopImmediatePropagation(), in fact, I don't really care about the event itself, for me it may be null - I don't need the contents of the event at all, but the scrollHandler() in such situation isn't getting called...

In .NET events commonly have property "Handled" this property is there to tell whether the event was already processed by a handler or not. However, you, as a developer have control over it - you may decide whether to process it further or not, but it seems like in AS3 you must bloat the player's memory with piles of useless clones of same event...

Any reasonable explanation to why it is like this in AS3?

IQAndreas
December 13th, 2009, 10:51 AM
I'm not sure why, Flash treats events this way, but in order for the code you posted to work, you need to literally create a NEW instance of event and apply whatever properties you want to it.


//BAD CODE
public override function clone():Event
{ return this; }

//GOOD CODE
public override function clone():Event
{ return new ScrollEvent(ScrollEvent.SCROLL); }
A quick Google search returned more info (cheers to Senocular!)
http://www.kirupa.com/forum/showthread.php?p=2098265


EDIT: You could actually do something along these lines (which is what I did for a recent project)

private function get _scrollEvent():ScrollEvent
{ return new ScrollEvent(ScrollEvent.SCROLL); }

public override function set scrollRect(value:Rectangle):void
{
super.scrollRect = value;
trace(super.hasEventListener(ScrollEvent.SCROLL)); // true
super.dispatchEvent(this._scrollEvent);
}


And I agree, Events should have a "handled" property, along with a bit more information. It's time I finally get around to rewriting the EventDispatcher class like I have always wanted to. :sigh:

wvxvw
December 13th, 2009, 11:27 AM
Yes, I found it out I need a "new" event each time I want to dispatch it - but I don't understand why is it so in player's design / why it is never mentioned / explained in the docs... If you look at your application in profiler, you'd notice how many events are created. Relative to how many you actually need that may be like 10 times more or even 100 times more...

EDIT:
http://bugs.adobe.com/jira/browse/FP-3456

EDIT2:
Writing your own EventDispatcher won't really help in such case - there are to many built-in objects that already are EventDispatchers with this exact behavior...

IQAndreas
December 13th, 2009, 11:59 AM
Hm... I tried digging through the Language Reference for an answer, and I stumbled upon this:

file:///C:/Programming/Flash/Docs/langref/flash/events/EventDispatcher.html#dispatchEvent%28%29
event:Event — The Event object that is dispatched into the event flow. If the event is being redispatched, a clone of the event is created automatically. After an event is dispatched, its target property cannot be changed, so you must create a new copy of the event for redispatching to work.
So according to Flash, Events are automatically redispatched, however, this is not happening... Perhaps it has to do with that by default, "clone()" will redispatch "Event" with the same properties. However, your listener function is expecting a "ScrollEvent" instead, so this causes a conflict, and a clone is not created.

Do you specifically need it to be a ScrollEvent? You could just dispatch a regular event, but with a constant named "SCROLL":
this.dispatchEvent(new Event(MyScrollBarClass.SCROLL));

This should allow you to auto-clone it and everything. However, it will still automatically create a new Event weather or not you use this system.

I'm still not finding a clear reason WHY...


Hm... I'm getting more and more ideas on my Event class. Just a little annoyed Flash doesn't allow you to manually create weak references.

wvxvw
December 13th, 2009, 12:44 PM
>> methods defined might actually require parameters and/or return values.

Nope, my ScrollEvent#clone() returns precisely an instance of ScrollEvent, although, it's an instance that was already dispatched once, but it's absolutely valid, I may just call the handler with that same event bypassing the dispatchEvent() function and it will work.

If I will use regular Event, I'll create it every time anew, which, in my case would mean at least 30 event-objects per second (and 60 if I have 2 handlers, in real-life example I have 4 handlers - for only one scrollBar), where I could easily do with a single object. My ScrollEvent isn't cancelable and has no default behavior, besides, the target and current target are already taken care of - they return proper values if and if not the event was cloned. (Meaning, the event doesn't bubble, so the target and currentTarget will be always the same thing).

PS. Weak references:

var d:Dictionary = new Dictionary(true);
var obj:Object = {};
d[obj] = true;
obj = null;
System.gc();
System.gc(); // if you're running 9.0.115 you need to call it 2 times xD
for (var o:Object in d) trace(o, d[o]);

senocular
December 13th, 2009, 01:39 PM
I'm not fully understanding the question, but I'm leaning towards the information in the link IqAndreas provided earlier having the answer: event clones are needed to define the target. If you re-dispatch event, that event's target will have changed. A single event cannot specify two targets so a new event is needed for the new target. Additionally, you won't want a single event object to represent different events because those objects themselves represent the action that has taken place. Are two actions the same? You would tell by comparing the event instances with each other. If you have two different actions that use the same event, in code they would be considered the same event making such a comparison fail.

This is how the default Flash Player event model works. It's not so complex that you can't replace it with a custom solution if needed. In fact if you're looking to optimize performance, you wouldn't be using events, but rather simple callbacks which are much faster. Additionally, I doubt that the creation of a single scroll bar event represents any kind of bottleneck for an application.

wvxvw
December 13th, 2009, 02:02 PM
Well, I cannot not use events - reason MXML.
Besides, saying that events aren't optimized, don't use them - doesn't quite help either... May I not want that the core player functionality would work better?
That's precisely the case when I do want the same object to represent different events of the same type. To make it even simpler - I don't care what is the argument of the event handler - this is because of how MXML codegeneration works (which is crap - but I'm not the one who will rewrite it all, sorry).
Besides, my event exposes alternative and more meaningful mechanism for comparison, if the need be, since it is a scroll event it has position property for example. And, honestly - it is not a problem to distinguish between 2 function calls if that's required. Which, so far I also never needed...
(The arguments array is created each time anew and it wraps over the event, so, if you really want to compare function calls - you can compare the arguments... which is again one of the reasons why method calls in AS are so incredibly slow... But, really, the task of comparison of two method calls, even though bizarre, and hardly ever useful may be achieved simply by calling getTimer(), or putting some counter into the function, or, worst case scenario - Function is a dynamic class - you can add properties to it directly).

Even though the event model of the player is not complex - I cannot replace it - I may only add either on top of it, or alongside of it.

Lastly, I don't understand why the event is not dispatched - well, if it doesn't bubble and you redispatch it (not through bubbling), the target is reset anyway, which makes currentTaget property meaningless. Besides, I can override target getter and return there anything I want, not even an IEventDispatcher - if that's what suites me better... so, there is absolutely no logic in not dispatching the "old" event... it's just again, an attempt to follow some abstract idea, which has little connection to the real-life needs...

PS. The scrollBar is just an example, but, you may imagine that the aplication doesn't consist of a single scrollBar, it'd have more components in it... Well, you know, when you see 5-6 digits numbers in profiler in cumulative instances field, generally you start tearing your hair out... but, somehow everyone believes that if those are events, then it is fine - why? What's so peculiar in events, so you let them multiply in such quantities?

IQAndreas
December 13th, 2009, 03:16 PM
I have a crazy idea. (I call it pure evil, now in "mini" size) Either by overriding or by creating your own methods and using IEventDispatcher, use your own event listener something along the following (abbreviated)

private var eventDispatcher:EventDispatcher = new EventDispatcher(this);

//To prevent errors, the "target" and "currentTarget" properties have been overriden
//to always return the "target" passed in to the constructor.
private const _scrollEvent:ScrollEvent = new ScrollEvent(ScrollEvent.SCROLL);

//Or = new Vector.<Function>();
private var _scrollEventListeners:Array = new Array();

public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
{
if (type == ScrollEvent.SCROLL)
{
//TODO: Use the priority property to order them in the array differently
_scrollEventListeners.push(listener);
}
else
{
eventDispatcher.addEventListener(type, listener, useCaptur, priority, useWeakReference);
}
}

//Do similar for "removeEventListener", "hasEventListener", etc!

//Call this function instead for a performace boost, avoiding the overhead of Events
private function onScroll():void
{
//This replaces "dispatchEvent", but only for ScrollEvent.SCROLL
for (var i:int = 0; i < _scrollEventListeners.length; i++)
{
_scrollEventListeners[i].call(_scrollEvent);
}
}
Something along those lines. You could even speed it up by only allowing one listener at a time, and instead use a public property "onScroll", similar to how AS2 handled events.


Of course, considering how Flash already creates and dispatches new Events every time it dispatches an ENTER_FRAME or MOUSE_MOVE event, it probably isn't that much of an overhead, and might actually be faster than the above method because it is built into Flash Player already, but I'm not sure.

The Events are garbage collected pretty quickly anyway, since they rarely get stored outside of the listener function.



Also, garbage collection using "System.gc()" only works in the DebugPlayer (sadly). :(

wvxvw
December 13th, 2009, 03:52 PM
Well, in such case I don't really need an additional EventDispatcher ;) I may just use the one that I already have - i.e. this / super + I could really implement all the weak references + priority stuff...

Not the useCapture (which is another thing that is broken, but that's a separate topic)

But, If I was already to rewrite it, I'd probably not use EventDispatcher mechanism, loose the useCapture parameter (I cannot implement bubbling on my own and have no need) for example and make an option for customization of handler's signature... Besides, I would really make event types enums, and not strings... Loose currentTarget along with stopImmediatePropagation as well...

What bothers me is: why is it not so in the player, well, even if by some design it may be so - why not give an option to do it otherwise?
So far I can hardly count even a single property of DisplayObject that works 100% of the time... width / height / scaleX / scaleY / rotation are screwed when you rotate an object by 90 degrees (you then cannot set the height of the object - setting both height and width will set the width)... all the 3D properties / methods are useless... scale9grid works only for not-rotated objects, Matrix has a bug with padding bit... mask is screwed when you apply filters... scrollRect doesn't let you get the original size of an object, even if you remove and reapply it within a single function, the dimensions aren't updated... addChild() adds a child already parented by another container silently... getObjectsUnderPoint() doesn't work for scaled objects... globalToLocal and localToGlobal + getBounds() will do something unpredictable in certain circumstances...
Now this thing with events...

Sorry for this wining... it's just that I'm in a kind of situation now, like when you fix one thing, and you get two things broken as a result of your fix, and it just never ends... :)

IQAndreas
December 13th, 2009, 04:35 PM
What bothers me is: why is it not so in the player, well, even if by some design it may be so - why not give an option to do it otherwise?
Mark this day as a commemorative one. One more person has opened his eyes and looked away from the blinding light of Flash, only to realize, they are nothing but headlights blinding to the truth that instead of a glorious car, all Flash is is a cheap Ford where everything needs repairing, but since they use their own standard, you are unable to procure any working parts that fit with Flash's hardcoded system, so instead you must devise workarounds which are shaky, slow, and leaky, just because Ford is too stubborn to make decent equipment or provide quality workarounds. You no longer look at the Ford and say, "Well, it works..." Instead you say "Hey, there is something wrong here, and it shouldn't work this crappily."

And just because "Well, everyone else is doing it." doesn't make it right.

Join my "Flash-Anti-Fan Club!" I thought I was alone, but finally someone else can see the truth as well. And since your paycheck isn't coming from Adobe (Hint hint, wink wink, nudge nudge...), you are allowed to tell this truth to others.


Sorry for this wining... it's just that I'm in a kind of situation now, like when you fix one thing, and you get two things broken as a result of your fix, and it just never ends... I don't mind. What you speak of is the story of my life. Far too many of my "Is this possible?" threads have resulted in a clear "No."



Hm... There was going to be a point to this thread. I had a few on topic thoughts. Darn. I forgot them in the excitement...

wvxvw
December 13th, 2009, 06:07 PM
There's one quote about it...

Many forms of Government have been tried and will be tried in this world of sin and woe. No one pretends that democracy is perfect or all-wise. Indeed, it has been said that democracy is the worst form of government except all those other forms that have been tried from time to time.
:)
Maybe it's somewhat overly optimistic in the given light... but, it's kind of like that, more or less...

wvxvw
December 30th, 2009, 07:48 AM
I've finally found the time to put all my wishes for AS3 events in one place.

So, if you feel like those changes are needed, please vote for this ticket:
http://bugs.adobe.com/jira/browse/FP-2690