getDefinitionByName & AS3 reflection
[ENGLISH]
Today i solved an AS3 reflection problem: I wanted to create dynamically a class instance, using a string as class name.
So, why have I to make my life so hard?
Obviously, for giving more elegance to RMGConnecter, the RIA we are currently developing. It is intended to be a platform that will include many services. We still don’t know most of these services: we will develop them in the early future. But now, we still don’t know their names.
So, how to build RMGConnecter for creating instances of not-yet-known services (contained in a simple Canvas component)? How to import them?
With reflection all becomes very simply:
- we insert main components of every services in a single directory (for example: RMGConnectService[component name].mxml)
- we will import these component with the following code:
import RMGConnect.Services.*;
- we will use the getDefinitionByName method from package flash.utils for creating a reference to one of these classes. Considering a foo name, “RMGTest”, the code will be:
var myClass:Class = flash.utils.getDefinitionByName("RMGTest");
var myComponent:* = new myClass();
this.addChild(myComponent);
Unluckily, Flex 2 Artificial Intelligence will include in compiled files just the classes that are explicitly used in the code. So, if i will not declare any RMGTest variable, Flex Builder 2 will be sure that i will have not the need of that class and will not include it in swf files.
Executing the code, the debugger will trace a ReferenceError: Error #1065.
This feature was studied just for excluding not-used classes in compiled files (that will surely be more optimized and faster to execute) but it will also exclude from including all classes referenced dinamically through function getDefinitionByName.
Solution
We can solve the problem creating a variable for each type/classes that we want to instanziate with the following code:
var fooRMGTest:RMGTest = null;
Now, Flex Builder 2 will be sure that we will use RMGTest class and it will include it in compiled files withouth giving us any error. Unluckely, we loose our elegance
.
[ITALIANO]
Oggi mi sono trovato ad affrontare un problema di reflection in ActionScript 3: volevo creare un’istanza di una classe dinamicamente, utilizzando una stringa contenente il nome della classe.
Ma perchè mi voglio complicare così tanto la vita?
Semplice, per una questione di eleganza. RMGConnecter, la RIA che stiamo sviluppando con tanto impegno, è una piattaforma studiata per essere implementata con una grande varietà di servizi.
Questi servizi verranno sviluppati nei prossimi mesi e ad oggi non abbiamo ancora le idee chiare su quali siano, quali funzionalità abbiano, come si possano comportare ma soprattutto non sappiamo come si possano CHIAMARE.
Come fare quindi a predisporre RMGConnecter per creare istanze di servizi che ancora non conosciamo (il cui componente contenitore è un semplice Canvas)? Come fare ad importarle?
Con la reflection tutto questo diventava assai semplice: si inseriscono i principali componenti di ogni servizio in una specifica directory (per esempio: RMGConnectServices[nome componente].mxml), si importano i componenti di tale directory con l’apposita sintassi:
import RMGConnect.Services.*;
e, grazie al metodo getDefinitionByName del package flash.utils, si crea un riferimento alla nostra classe (chiamata “RMGTest” in questo esempio):
var myClass:Class = flash.utils.getDefinitionByName("RMGTest");
var myComponent:* = new myClass();
this.addChild(myComponent);
Purtroppo l’intelligenza artificiale di Flex Builder 2 vincola l’inclusione nei file compilati soltanto alle librerie effettivamente usate nel codice: nell’eseguire il codice il debugger ci restituirà quindi “ReferenceError: Error #1065“.
Pertanto, se non dichiaro alcuna variabile esplicitamente di tipo RMGTest, Flex Builder 2 intuirà che io non avrò bisogno di tale classe ed escluderà in questo modo dal file compilato.
Tale funzionalità, studiata appositamente per non appesantire i file compilati con librerie mai utilizzate, esclude però dalla compilazione tutte quelle classi istanziate dinamicamente tramite la funzione getDefinitionByName.
La soluzione?
Creare una variabile per ogni tipo di servizio che vogliamo istanziare:
var fooRMGTest:RMGTest = null;
Ora Flex Builder 2 sarà sicuro del fatto che noi utilizziamo tale classe, la includerà nel file compilato e non restituirà alcun errore di istanziamento in fase di esecuzione. Purtroppo sarà necessario aggiungere una variabile per ogni tipo di servizio implementato.
Thanks for this. You’re right. It’s not very elegant but it solved my issue.
j-sizzle
November 1, 2007 at 12:34 am
Thanks for that tip, its been driving me mad for the past hour, as im
creating instances from dynamic XML.
Create a null instance and the Class is registered.
n king
November 20, 2007 at 9:09 pm
Thanks a lot.
I was getting 1065 and I didn’t know why. Your post saved me a lot of time!
Michal Kuklis
December 4, 2007 at 5:15 am
The same situation is with Flash CS3 compiler, so this is not only Flex problem
Great post, I had same problem and described it on my blog.
Goran Grubic
January 4, 2008 at 3:41 pm
What do you mean ?
music
January 30, 2008 at 10:34 am
I think you might like to know that there is a more elegant solution for this. I too hate the hackiness in having to instantiate a dummy var in the application.
Check this out – http://jwopitz.wordpress.com/2008/02/21/getdefinitionbyname-trick/
jwopitz
February 22, 2008 at 12:25 am
[...] us – actionscript contains a handy little utility called getDefinitionByName. Unfortunately for us, there are a few issues with [...]
Paul Mignard » Blog Archive » Giving up elegance for speed - a tale of Flex Builder 3 woes…
August 29, 2008 at 3:42 am
Thank you, it solved my problem.
Very usefull !
fanch
April 22, 2009 at 1:15 pm