September 13, 2009

AS3 Signals Getting Stronger

A lot has happened in the last four days.

What Happen? We Git Signal


The majority of commenters preferred Github to Google Code so I moved the Signals project there:

http://github.com/robertpenner/as3-signals

I am experimenting with mirroring back to the Google Code SVN. It's going ok so far.

Committing Early and Often


I made about 20 commits to Github this weekend. Some highlights:
  • The package is now org.osflash.signals (previously com.robertpenner).
    I wanted it to have more of a community feel. Thanks Aral for the namespace!
  • Listener priority is now supported in ISignal.add().
  • ISignal.dispatch() can now send any number of arguments to listeners.
    Zero or ten, it's up to you.
  • Various classes and APIs were renamed for clarity.
  • More unit tests and fixes.

Going Native


Since my last post about connecting EventDispatchers to Signals, I had another idea for integration:

Why not use EventDispatcher for the actual dispatching but wrap it in a Signal facade?

Presenting the NativeSignal class, which lets you have your cake and eat it too.
  1. Take any EventDispatcher, e.g. Sprite.
  2. Create a NativeSignal that targets an event of the dispatcher:
    // in a subclass:
    click = new NativeSignal(this, 'click', MouseEvent);
    
    // or decorating an instance:
    click = new NativeSignal(theDispatcher, 'click', MouseEvent);
  3. Enjoy the Signal APIs and features.
  4. Dispatch from the NativeSignal or the EventDispatcher. Both use Flash's native dispatchEvent()
If you're hesitant to put your trust in new dispatching code, or you want to keep your EventDispatcher options open, this is the gateway drug for you. You don't have to give up anything. All the native functionality stays, and the ISignal interface can be piped in like frosting, wherever you like. Doesn't that sound delicious?

Get Connected

September 9, 2009

The Community Responds to AS3 Signals

The community response to my Signals event system has been very encouraging. In the two days since I opened it to the public, there have been dozens of comments on my announcement and re-tweets. People have been quite positive and interested. No one has told me I'm wasting my time (you could be the first!).

The best part has been people's insights for improving Signals. I'm been rapidly integrating them as you can see in my commits for the last two days. A special thanks goes out to Richard Lord for helping me understand AS3 event bubbling and how to implement it in Signal.

Connecting EventDispatchers to Signals


[EDIT: I renamed EventDispatcherSignal to NativeRelaySignal]

I received a very clever idea by Jacob Wright for relaying EventDispatcher events to Signal listeners. I implemented it in a new EventDispatcherSignal NativeRelaySignal class. It lets you easily wire, say, a Sprite to send native events through your own signals. The same goes for any object that implements IEventDispatcher.

Here's how you could create a Sprite subclass that uses a Signal for the click event:

public function MySprite()
{
 click = new NativeRelaySignal(this, 'click', MouseEvent);
}

// listen elsewhere
function onClick(e:MouseEvent):void { ... }

mySpriteInstance.click.add(onClick);
// fire a MouseEvent manually to test
mySpriteInstance.dispatchEvent(new MouseEvent()); // onClick is fired

The implementation of wiring the Signal to the IEventDispatcher is very simple:

[EDIT] I added listener count checking to avoid bugs (thanks to Richard Lord).

// in NativeRelaySignal.as
override public function add(listener:Function, priority:int = 0):void
{
 var prevListenerCount:uint = listeners.length;
 // Try to add first because it may throw an exception.
 super.add(listener);
 // Account for cases where the same listener is added twice.
 if (prevListenerCount == 0 && listeners.length == 1)
  IEventDispatcher(target).addEventListener(_name, dispatch, false, priority);
}

Also, Jacob's twin Tyler sent me some mind-bending ideas for radical memory efficiency. He takes lazy instantiation to a whole new level. I'm still reeling from thinking about things like Signal inheriting from Array and using a getter's arguments.callee as an index into a Dictionary. We'll see how this evolves.

Github vs Google Code


Question: Who would prefer using Github for the Signals project, rather than Google Code? I've used Git a bit and would be open to moving to it if contributors preferred that.

September 7, 2009

My New AS3 Event System: Signals

I have more critiquing of AS3 events I could do, but I figured I'd throw my hat in the ring.

I've been working on something I call Signals. It's a new approach for AS3 events, inspired by C# events and signals/slots in Qt.

Today I created my first-ever Google Code project:

http://code.google.com/p/as3-signals/

EDIT: I moved the project to Github. http://github.com/robertpenner/as3-signals

I listed features with short code samples there. I'm having a hard time pasting them into Blogger without re-doing it all. Here are the Concept and Philosophy sections for a start:

The Concept

  • A Signal is essentially a mini-dispatcher specific to one event, with its own array of listeners.
  • A Signal gives an event a concrete membership in a class.
  • Listeners subscribe to real objects, not to string-based channels.
  • Event string constants are no longer needed.

Philosophy

  • Composition and interfaces are favored over inheritance.
  • Events in interfaces are a good thing.
  • Event types are classes, not strings.
  • Event classes should focus on the data they contain, not on who sends them.
  • Event classes should not contain string constants that various other classes use.
  • Test-Driven Development (TDD) is essential and fun.
  • So far, not a single static keyword in the source.
  • No singletons.

The Project Home Page has the feature listing and code examples.

I want Signals to be really good, and I'm open to changing anything and everything to improve it. So please check it out and post your impressions, critiques and suggestions.

September 6, 2009

My Critique of AS3 Events - Part 2 - The Dispatchening


Come with me as I chase more windmills.

flash.events.EventDispatcher could be easier to clean up


removeAllEventListeners(), where are you? Everyone's been looking for you.
And wouldn't it be great if I could mark some listeners to be removed automatically the first time called?

Whatever, this is OOP, it shouldn't be too hard to subclass and do it myself.
Let's create a new class, override addEventListener() and...

flash.events.EventDispatcher is difficult to extend


Oh snap, looks like my subclass can't access all those listeners in EventDispatcher.
Guess I'll maintain my own array of listeners so I can remove them all easily. It's been done before:

http://k2xl.com/wordpress/2008/07/02/as3-eventmanager-class-removealllisteners/
http://code.google.com/p/k2xl/source/browse/trunk/as3classes/src/util/k2xl/EventManager.as

But now my array has strong references to all the listeners. The useWeakReference option won't help me now.
Ohhh, I know!
Dictionary
can haz weak references?
Theoretically, yes.
Unfortunately, though, storing methods in a weak keys Dictionary is buggy.

Besides, Dictionary has no concept of order.
What if I need to know what sequence the listeners are in?
Would I create a weak reference Dictionary for each listener and have an array of dictionaries?
It's been done, but I'm not going to consider that until the Dictionary bug is fixed.
Hmm... Can haz special array with weak references? Keep dreaming.

The point being, flash.events.EventDispatcher is a black box--the frustrating kind. Adobe Flash Player engineers, if you're reading this and you know a magic namespace to open up the listeners, please let me know. While we're dreaming, how about a look at the source code for EventDispatcher?

Oh well, at least we have the IEventDispatcher interface. It seems like a lot of work, but I should be able to write my own dispatcher from scratch and satisfy the interface, right?

IEventDispatcher cannot be implemented


Not entirely true. It can, but not with pure AS3. But I don't know that yet.

So I work away building my own implementation of IEventDispatcher. I'm adding listeners, I'm removing listeners, I'm even removing all listeners! Things are looking up.

I'm at the exciting part now: the dispatchEvent() method. Ok, find the listeners array for the event type, good, good. Now I'll just set the event.target property to this and iterate through the--

GACK!

Event.target is read-only


NOOOoooooo!!! 

I can picture the meeting in the secret DOM Level 3 Dungeon:

Level 3 Paladin:  "Would anyone ever need to change the event target?"
Level 3 Seer"Inconceivable!"

But, there must be some way of changing the event target, right?

IEventDispatcher cannot be implemented without using EventDispatcher


You see, flash.events.Event has a secret alliance with flash.events.EventDispatcher.
Only EventDispatcher is entrusted with the awesome power to change target.
We're so much safer that way.
And yes, you will have to extend flash.events.Event because there is no event interface.

Here's an offer you can't refuse: implement an interface using one special implementation of that interface!

You can write your own dispatcher if you like, but if you have a hang-up about target and currentTarget being null all the time, you'll have to instantiate EventDispatcher and have it dispatch the event for you. Which means it will need all your listeners and your Gmail password. Which makes you wonder why you even bothered.

I hope this gives a sense of the obstacles that arise when trying to extend the AS3 event system. It fights you at every turn. Please let me know if I've overlooked or misunderstood anything.

At least this article was fun to write. It solidifies my rationale for building and using my own event system when I don't need to integrate with the display list.

AS3 Events - 7 things I've learned from the community

Since posting my initial critique of AS3 events, I've learned a fair bit through community feedback. Thanks everyone for your comments and corrections; I edited my original post accordingly. Conversations on Twitter brought out further subtleties of how developers work with AS3 events.

I still have more critique in me, but first I'll eat a little humble pie. I haven't actually worked much with custom events in the display list. Most of my AS3 development has been in code libraries that operate independently of the display list. Thus, my custom events generally don't need to bubble or cancel. So when I look at the AS3 event system, I see APIs that often add clutter without a benefit to my project. For developers building RIAs, AS3 event capturing, targeting, bubbling and canceling is wonderful. The standard is called DOM Level 3 Events for a reason. It's great if you're in a DOM, but that doesn't mean it's the most usable solution for events in general. But I'll have to leave that discussion for the next critique.


What I've learned about AS3 events in the last 3 weeks


  1. Custom events can bubble when you dispatch them from a DisplayObject.
    I'd never tried this, and thought that only Flash Player events (MouseEvent, etc.) could bubble.

  2. Custom events can be canceled.
    The Event APIs cancelable, preventDefault() and isDefaultPrevented() are not just for Flash Player events.

  3. Despite Grant Skinner's argument for using weak listeners, some experienced developers choose not to.
    Some say relying on weak references has caused more problems than anticipated. However, Grant advocated always removing listeners explicitly. Weak references are just "an added level of security".

  4. Flash Player 9 didn't always garbage collect weak references.
    This is fixed in Player 10.

  5. Storing method references in a weak keys Dictionaryis buggy.
    References may be duplicated or garbage collected prematurely. Technically, this isn't part of AS3 events. But when trying to extend the event system, you may use a Dictionary to store listeners or callbacks. Developers who've done this have learned not to use weak keys.

  6. When an event has no listeners, EventDispatcher.dispatchEvent() is unnecessarily slow.
    Grant Skinner's patch is a 5x speedup, apparently. 

  7. Some say listener priorities are smelly.
    Like MovieClip.depth in AS2, priority numbers introduce dependencies into your code. These become increasingly difficult to manage in larger systems, as new code must take into account the  priorities in existing code.

    An alternative is to rely on the order the listeners are added. The original dispatcher can add itself as a listener first and thus ensure it has first crack at the event. Unless a different listener misbehaves and steals the spotlight with a higher priority...

    This is an interesting one. I haven't had to deal with this issue and I'm not sure what I think yet. The listener order does start to feel similar to the stacking of movie clips in AS2. Who's going to come out on top? Do we need an equivalent of getNextHighestDepth() for listener priorities [shudder]? I'm reminded of how the Macromedia V2 components would grab the highest possible depth with its own depth manager, rendering getNextHighestDepth() useless. Is there a word for bad nostalgia? How about "nastalgia"?

August 16, 2009

My Critique of AS3 Events - Part 1

I've been thinking about AS3 Events a lot lately. I'm finding them a little bit annoying.

Don't get me wrong. After so many changing event models in Flash 5 through 8 (4 if tell target counts as an event), having one formalized in Flash Player 9 was a huge relief. I'm also aware that Macromedia didn't invent this event model. They used a standard: DOM Level 3 Events.

But even standards have room for improvement, on occasion. What follows is a good-natured roasting.

Event constants are inconsistent


Let's say I'm creating my first custom event, and I want to name it according to standard conventions.
What better place to start than the constants in flash.events.Event, beginning with "A":
  • ACTIVATE (verb, present tense)
  • ADDED (verb, past tense)
That didn't take very long. Past or present? Pick a side; we're at war. War with time-traveling virtual machines.

There must be a hidden logic to this. Perhaps I can decipher the pattern by looking at DisplayObject events:
  • render - Dispatched when the display list is about to be updated and rendered.
Oh, I see! It's present-tense because the render hasn't happened yet.

Wait, what's this?
  • removed - Dispatched when a display object is about to be removed from the display list.
I give up. Time paradox = retcon fail.

addEventListener() ordered options awkwardly are


Here is EventDispatcher.addEventListener() in all its glory:
public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void

A general goal in designing a method is to arrange the parameters from the most important or frequently used to the least.

Think fast!
Which optional parameter of addEventListener() do you use the most?

I'll bet you dollars to donuts you said useWeakReference. If not, you need to heed the G. [EDIT: Many have told me it's better to use strong references and meticulously clean up after yourself. I don't use weak listeners anymore.] When you do, you'll get to type this, over and over:

addEventListener(yada, yada, false, 0, true);
addEventListener(yada, yada, false, 0, true);
addEventListener(yada, yada, false, 0, true);

You'll say to yourself: "One of these days, I'm going to write true, 0, true. I just know it."

Tech interview question!
How often do you useCapture with your custom events? [EDIT: I was thinking of events not dispatched from a DisplayObject.]

[Dwight voice]
Trick question. It is impossible. FACT: Only DisplayObjects can have a capturing phase and a bubbling phase in addition to the targeting phase.

But impossibility doesn't mean I have to forget about them! The useCapture will be with you, always.

Event forces irrelevance on you


Want to create your own event class? No problem! You can do whatever you like, as long as what you like is to extend one special class. "Interface is hard!" says Developer Barbie.

Are you wondering how these exotic and intriguing APIs can benefit your custom event class?
  • bubbles
  • currentTarget
  • eventPhase
  • stopImmediatePropagation()
  • stopPropagation()
[EDIT: Sönke and Matthew commented that the "default" APIs can be used in non-DisplayObjects.]

Keep wondering! You don't get to use them. Players Club only.

[EDIT: To clarify, I'm saying that these APIs are applicable only to events dispatched from DisplayObjects.]

But look on the bright side. The code examples sucked anyway.

[EDIT: Here's a good article by Darron Schall: Creating Default, Cancelable Event Handlers.]

Sorry, that's not exactly true. If you follow the official recommendations, you'll spend time using these APIs even though they're no use to you.
  • You'll put bubbles and cancelable in your constructor and pass them to super().
  • That means you'll get to put bubbles and cancelable in your clone() override as well.
  • Top it off by overriding toString(), bringing in bubbles, cancelable, and (oh, neat!) eventPhase.
  • Now your event class looks busy. Busy equals important, right?
These are Adobe's documented recommendations. But all this typing has no benefit to you. None. If I missed something, please comment and enlighten me.
[EDIT: Commenters have pointed out that cancelable can be useful. But there's no need to put it in the constructor if you're not going to use it.]

I don't want this to sound like a big whinge. I mean, really, this stuff isn't horrible. When it comes to events, AS3 is way better than Java events. But not as good as C#.
To be continued.

April 30, 2009

Benefits of AsUnit XUL UI and solving Vista problem

AsUnit is my preferred unit testing framework for ActionScript 2 and 3. For years, I used just the AsUnit framework classes and wrote the unit tests myself. I didn't understand exactly what the "XUL UI" did or why I would use it.

But when I finally gave it the old college try, I found that the AsUnit application was quite practical and sped up my workflow. The XUL UI automates some repetitive tasks of Test-Driven Development (TDD):
  • Creating a new class: com.example.Widget
  • Creating package folders as necessary: com and example
  • Creating the unit test class: com.example.WidgetTest
  • Creating AllTests suite classes in every package folder that contains unit tests

Practically, this means that when I need a new class, I fire up the AsUnit app, type in com.example.Widget, and in seconds I have code skeletons ready to go. It's so much easier to be disciplined about writing tests first when they exist right off the bat.

For more instructions, see this excellent AsUnit tutorial by Tim Beynart.

AsUnit for Windows


If this is at all convincing, you can download the AsUnit XUL UI Windows installer here. (This link is gone from asunit.org at the moment, while Luke Bayes is reorganizing the site.)

AsUnit for OS X


If you're on the Mac, you can download an AsUnit OS X installer provided by Aral Balkan. The build is a little out-of-date, missing a few minor features added since then. However, Aral graciously wrote out his steps for creating the app at the end of his post. Luke is into Git now, so the source for XUL UI is in github.

AsUnit for Vista: Problem and Solution


When I tried to install the AsUnit XUL UI on a Vista machine, the app refused to launch. It seemed as though Vista's increased security was causing problems with XULRunner. The best solution I found was by rhoq on the FlashDevelop boards. You simply create a shortcut to the .exe with the following in the target field:

"C:\Program Files\AsUnit\xulrunner\xulrunner.exe" -app ../xului/application.ini

My shortcut has this in the Start in field, which seems to be necessary: 

"C:\Program Files\AsUnit\xului\"

Obviously, adjust these to your particular install path.

A Parting Thought


I do Test-Driven Development for many reasons, but mainly because it's fun. Writing tests first is 100 times more enjoyable than writing tests after production code. I'm not even joking when I say "it feels like rock climbing." I'll try to explain this in a later post.

April 26, 2009

Live Twitter from FITC Toronto

I'm attending the always excellent FITC conference in Toronto. Main sessions started today and I will be posting on Twitter live, provided the wifi stays up (a bit spotty so far). I'll be using the #fitc search tag.

http://twitter.com/robpenner

So far I've attended Alec Cove's Beautiful Algorithms nature-inspired session, and am in the middle of Mario Klingemann's Tinkerer's Box.