ActionScript.org Flash, Flex and ActionScript Resources - http://www.actionscript.org/resources
ActionScript 3.0 Best Practices: Using the EventCollector Class
http://www.actionscript.org/resources/articles/1098/1/ActionScript-30-Best-Practices-Using-the-EventCollector-Class/Page1.html
Pascal ECHEMANN
I'm an ActionScript developer in the French Riviera.
I've created the Swing Package for ActionScript 3.0 (SPAS 3.0) which helps Flash developers to easily create RIAs with the Flash Platform and both Flash and Flex:
http://www.flashapi.org/ 
By Pascal ECHEMANN
Published on June 22, 2011
 
Difficulty: Easy
Time Taken: 20 minutes
Description: In this article we will learn how to use the EventCollector class, for easily preventing memory leaks due to events management in ActionScript 3.0.

1) Introduction

A few weeks ago, I worked on a project based on the Youtube AS3 API and I was really surprised how Flash developers still have concerns with event management.
When I started the migration from SPAS 2.0 to SPAS 3.0, in 2006, one of the first classes I created was the EventCollector class. This class has been especially designed to solve these problems. So I have decided to share it within an autonomous package, even if it is still a core part of the SPAS 3.0 API. That means the EventCollector class is now compatible with Flash, Flex (Flash Builder and Flex SDK) and all of the available ActionScript 3.0 APIs and projects on the Web.

2) Requirements

In order to go through this tutorial, we assume that you have an ActionScript 3.0 environment set-up and that you are familiar with the basic concepts of event handling.

User level:

Beginning.

Prerequisite knowledge:

  • Experience in developing ActionScript OOP applications.
  • Experience in using SWC ActionScript libraries.
  • Experience in using an ActionScript 3.0 objects profiler is a plus but not necessary.

Products:


3) Events and chaos

The event DOM-based model, introduced with ActionScript 3.0, was a fantastic improvement for building complex applications. (Maybe some of you guys remember the good old Delegate class.) But if we pay attention to the memory leaks issues in ActionScript, arguably the persistence of object references through events is responsible in most of the cases. Because the event model is very flexible and easy-to-use, event listeners are often disseminated all over the code, without any rules or well defined structures. The fact is that if you implement a large number of unorganized lines of code within an application, you introduce "chaos in the computer"!

Now, just imagine a complete on line music editor, with tens of modules called, created, and displayed on the fly. Each module is composed of many different classes which are responsible for synchronous or asynchronous treatments (loading and saving data, playing back music, checking for music theory rules, etc.). Such an application cannot be developed if you do not take care of memory usage. Unfortunately, there is no way to easily fix event management issues. Each event listener must be removed before deleting unused modules and this could become really fastidious when you have to check hundreds of classes.

4) The EventCollector class

As we commonly say: "The simplest ideas work the best."
And the event collection concept is probably one of the most simple reusable piece of code ever written in ActionScript.

We know, that (fortunately) there are no mechanisms to automatically remove event listener references when setting an object to null. But if we consider the process of deleting an object, we would never have to know the number or type of event listeners associated to it and what they are doing. Our only concern should be to ensure their destruction.
That is precisely what the EventCollector class does! So, it has been designed with two main goals:

  • provide a convenient API to gain more time and write well-structured code,
  • ensure to delete event references in all cases.

Moreover, the EventCollector class  has been the SPAS 3.0 core event manager since 2006, which means that its robustness, efficiency and flexibility have been well-tested for many years.


5) Basics

Before exploring the class API, remember that EventCollector instances must be accessed from any part of your object, but always hidden from the public API. (See Chapter 8 for exceptions.)

Most of the time, we will only use two methods for managing events: addEvent() and removeEvent().

All parameters of the addEvent() method are the same as the parameters of the addEventListener() method, defined by the IEventDispatcher interface, except the first one, which is the listener object itself.

[as]addEvent(obj:IEventDispatcher, event:String, func:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void[/as]

Note that the event and the func parameters of the addEvent() method represent respectively the event and listener parameters of the addEventListener() method.

The common use of the EventCollector class is shown below:

[as]// Creates a new EventCollector instance var evtColl:EventCollector = new EventCollector(); // Creates a new Loader instance var loader:Loader = new Loader(); // Adds a new event handler to the event collection evtColl.addEvent(loader.contentLoaderInfo, Event.COMPLETE, loaderComplete); function loaderComplete(e:Event):void { trace("Data loaded"); }[/as]

The following code does the same without using an EventCollector instance:

[as]// Creates a new Loader instance var loader:Loader = new Loader(); // Adds a new event handler to the loader object loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderComplete);[/as]

To remove the registered event handler, you call the removeEvent() method with only three parameters: the listener object, the event type and the associated function. The code below shows how to remove the event handler we added above:

[as]evtColl.removeEvent(loader.contentLoaderInfo, Event.COMPLETE, loaderComplete);[/as]

This is the minimal API you need to know to work with the EventCollector class.
The event collection process provides many more methods to implement mechanisms based on the IEventDispatcher interface (e.g. hasRegisteredEvent()), or custom convenient methods (e.g. addEventCollection()). For more information about methods of the EventCollector class, go to the SPAS 3.0 documentation at: http://www.flashapi.org/spas-doc/


6) Benefits of using the EventCollector class

In the previous section we saw that the EventCollector class behaves exactly as objects that implement the IEventDispatcher interface do. At this point, it does not solve the problem raised earlier. For example, let's imagine an object which is responsible for creating and deleting a random range of particle objects. The following sample class illustrates such an object:

[as]package {          import flash.display.Sprite;     import flash.events.Event;          public class RandomEvents extends Sprite {                  public function RandomEvents() {             super();             init();         }                  private var _sprite:Sprite;                  private function init():void {             createObj();             deleteObj();         }                  private function createObj():void {             var i:int = Math.round(Math.random() * 100) + 1;             _sprite = new Sprite();             for (; i >= 0; i--) {                 var child:Sprite = new Sprite();                 child.addEventListener(Event.ENTER_FRAME, fooBar);                 _sprite.addChild(child);             }         }                  private function deleteObj():void {             _sprite = null;             // Child sprite objects can not be garbage-collected because             // event are still fired.         }                  private function fooBar(e:Event):void {             trace("Called");             /*             // Sample random action:             var tgt:Sprite =  e.target;             tgt.x =  getRandomPos();              tgt.y =  getRandomPos();             */         }                  private function getRandomPos():Number {             return  (Math.random() * 1000);         }     } }[/as]

In this example, the createObj() method creates a random number of sprite objects and adds them to the display list of the _sprite instance. Then, the deleteObj() method sets the _sprite instance to null.
If you do not associate some event listeners to its children, setting the _sprite instance to null will make all the contained sprite objects eligible for garbage collection. But as we have added an event listener to each child, all of them remain in memory.

This class reproduces a common coding case often encountered across the Flash community.

The best way to prevent the issue  described in this example is to use the removeAllEvents() method provided by the EventCollector class. As we said in the previous chapter, welldesigned applications imply that we do not need to know anything about the registered events; we just have to remove all of them. When calling the removeAllEvents() method, all events listeners registered through the EventCollector instance are automatically deleted for all added objects.

Replace the preceding deleteObj() method by the following one and launch the Flash Builder, or FlashDevelop, "objects profiler" to compare the result in both cases:

[as]private function deleteObj():void {     _evtColl.removeAllEvents();     // Deletes all event listener references.     _sprite = null;     // All sprite objects can be garbage-collected and memory being free. }[/as]

This is probably the most simple and convenient mechanism to prevent memory leaks due to event management.
As you can see, the EventCollector class respects the assumptions set out in Chapter 4: gaining time and ensure deleting event references in all cases.


7) Best practices for Modular OOP in the Flash Platform

The sample class in Chapter 6 is probably too short and too basic to illustrate the real benefits of using the EventCollector class for managing all of your events within a Flash application.

In Chapter 3, we talked about managing "hundreds of classes". And inevitably, this leads us to think about how we design our applications on the Flash Platform. Remember what we said above: "The simplest ideas work the best." Bertrand Meyer introduced Modular OOP about 15 years ago [MEYER, 1997]. As everything has already been theorized, we just have to apply some of these concepts to make better software.

By combining modular design (consider classes as modules) and a few simple techniques, such as the EventCollector class, it should be possible to definitely close discussions about memory leaks on the Flash Platform. A base rule should be accepted by everyone:

Each time a main object is responsible for creating a strong reference in the memory to any other object, it should provide a visible mechanism for removing this reference.

This is a global concept, so "visible" means "accessible by the consumers of the main object".

For example, SPAS 3.0 API implements such a process through the finalize() methods. According to this, if we consider the sample class from Chapter 6, the deleteObj() method should be rewritten as shown below:

[as]private function deleteObj():void { _evtColl.removeAllEvents(); // Deletes all event listener references. evtColl.finalize(); // Deletes all internal persistent objects used within the EventCollector instance. evtColl = null; // Deletes the EventCollector instance. _sprite = null; // All sprite objects can be garbage-collected and memory being free. }[/as]

For a more global transcription, regarding ActionScript 3.0 practices, the original rule could be simplified to this one:

Each time a class instance is responsible for creating a BitmapData instance or an event listener, it should provide a public method for deleting the reference of this object.

Moreover, the logical name of such an action should be "destroy". And that is where the EventCollector class is really interesting to use, because we no longer need to pay attention to the event listener references we have created to delete them later. A single command is used to carry out this task:

[as]public function destroy():void { _evtColl.removeAllEvents(); _evtColl.finalize(); _evtColl = null; // Write the rest of the destroying statement here... }[/as]

By applying this code snippet to all of your developments, you could create much better reusable libraries, components or applications.


8) EventCollector and MVC

When you are designing an object-oriented application, it is not advisable to share functionalities across several different modules. The direct consequence is that sharing an EventCollector instance, between two or more classes, is not good practice.

However, it is always possible to implement a shared EventCollector instance in the MVC pattern. But to be efficient, you must follow some basic rules to design the structure of the pattern:

  • The EventCollector instance must be a singleton.
  • The MVC pattern should be considered as a module itself. It means that EventCollector singleton must not be used by an object which does not belong to this module. (Principle of Retention of Information.)
  • The removeAllEvents() method must be the first action called within the destroying method implemented by the model. So, the structure of events management inside each object must be designed to respect this rule. (This is based on the principle of Modular Composability.)

Even if it is technically possible to do that, it seems wise to discourage the use of such practices! Unfortunately, the MVC pattern is not the best way to simplify the management of memory leaks. Thus, whatever the considered solution, it will not be easy to implement it.

In any case, you should implement a destroying method each time the rules introduced in Chapter 7 are confirmed.

9) Where to go from here

In this article, we learned how to use the EventCollector to simplify and improve the management of event handling in ActionScript 3.0. By now, you should be able to implement the EventCollector API in your existing and new projects by using the concepts explained in Chapter 7.

A Javascript portage of this class is available on the EventCollector page.

To be informed of new releases, latest information and documents about the collector API, or support SPAS 3.0 projects, follow us on the SPAS 3.0 Facebook page at: http://www.facebook.com/flashapi

We need your feedback to add functionalities to the collector API, so feel free to post your comments on the page devoted to this article, on the SPAS website at: ActionScript 3.0 Best Practices: Using the EventCollector Class

10) Bibliography

[MEYER, 1997] Bertrand MEYER, Object-Oriented Software Construction, second edition, Prentice Hall, 1296 pages, January 1997.