Proceed With Caution: Adobe Flex Inline Functions in Loops
Posted on October 21, 2007 | Filed Under Adobe Flex, Software Engineering
I recently ran into a problem with Adobe Flex inline functions that I want to share with you. I spent a good part of a day trying to find the problem in my code only to discover an unexpected ECMAScript-spec’d behavior was to blame. Take heed of this if you use inline functions inside a loop.
I was writing code that would parse a screen layout description and create the layout dynamically at runtime. To do so, I needed to instantiate several UIComponentDescriptor objects.
The UIComponentDescriptor class is used to store the properties, styles, effects and event handlers assigned to objects in MXML code. It is used as part of the MXML to AS conversion process when a Flex application is compiled. All MXML tags are converted to ActionScript during the compilation process.
The UIComponentDescriptor class contains a propertiesFactory member property. It expects a Function that will return an Object when called. The MXML compiler generates an inline function that returns an Object when it converts MXML tags to ActionScript code. Here’s an example of the code that the MXML compiler produces:
-
// Original MXML code
-
<?xml version="1.0" encoding="utf-8"?>
-
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300">
-
<mx:Label x="10" y="10" text="This is a sample component"/>
-
<mx:ArrayCollection id="theData">
-
<mx:String>AK</mx:String>
-
<mx:String>AL</mx:String>
-
<mx:String>AR</mx:String>
-
</mx:ArrayCollection>
-
<mx:HBox x="10" y="36" width="100%">
-
<mx:ComboBox dataProvider="theData"/>
-
<mx:List width="100%" dataProvider="theData"/>
-
</mx:HBox>
-
</mx:Canvas>
-
-
// UIComponentDescriptor portion of MXML Compiler generated code
-
private var _documentDescriptor_ : mx.core.UIComponentDescriptor =
-
new mx.core.UIComponentDescriptor({
-
type: mx.containers.Canvas
-
,
-
propertiesFactory: function():Object { return {
-
width: 400,
-
height: 300,
-
childDescriptors: [
-
new mx.core.UIComponentDescriptor({
-
type: mx.controls.Label
-
,
-
propertiesFactory: function():Object { return {
-
x: 10,
-
y: 10,
-
text: "This is a sample component"
-
}}
-
})
-
,
-
new mx.core.UIComponentDescriptor({
-
type: mx.containers.HBox
-
,
-
propertiesFactory: function():Object { return {
-
x: 10,
-
y: 36,
-
percentWidth: 100.0,
-
childDescriptors: [
-
new mx.core.UIComponentDescriptor({
-
type: mx.controls.ComboBox
-
,
-
propertiesFactory: function():Object { return {
-
dataProvider: "theData"
-
}}
-
})
-
,
-
new mx.core.UIComponentDescriptor({
-
type: mx.controls.List
-
,
-
propertiesFactory: function():Object { return {
-
percentWidth: 100.0,
-
dataProvider: "theData"
-
}}
-
})
-
]
-
}}
-
})
-
]
-
}}
-
})
A lot of ActionScript code is generated behind the scenes during the MXML conversion process!
Since I was creating things dynamically at runtime, I needed a little different approach. My first attempt, which exposed the unexpected behavior, was a pretty simple one:
-
// Iterate layout XML
-
for each (var item:XML in layoutInfo.*)
-
{
-
// Create specified item
-
var childDescriptorProperties:Object = new Object();
-
var propertiesFactory:Object = new Object();
-
childDescriptorProperties.propertiesFactory =
-
function():Object { return propertiesFactory };
-
// Populate propertiesFactory object with properties specified in XML data
-
…
-
}
A new instance of the childDescriptorProperties and propertiesFactory objects are created during each loop iteration. Likewise, a new inline function that returns the current instance of the propertiesFactory object is also instantiated. It should be “all good”, right? Unfortunately, no. I compiled and executed the code and found my dynamic layout was seriously messed up.
So, where did the problem lie? I meticulously traced the code several times and everything appeared to be working exactly as I expected. Yet the final result was always the same distorted mess.
I finally resorted to writing down the memory addresses of every Object and Function being instantiated and verifying them just before calling the createComponentFromDescriptors(…) function to instantiate my layout. That’s when the problem became obvious. Although each iteration resulted in unique addresses for both the Objects and Function, the Object being returned by the Function was the same across every instance of the Function. It always returned the Object that was created by the last loop iteration.
As Ely Greenfield pointed out in the comments below, this behavior is defined by the ECMAScript standard. Please read his comment below for a complete explanation.
The good news for me was that I could look for a different way of doing what I needed now that I understood the problem source. My solution was to implement a new class that would contain a member property to store the propertiesFactory object and a member method that would return the member property. I then stored a reference to the member method as my childDescriptorProperties.propertiesFactory function to be called and the problem was solved.
I’ve included a simple example of the problem and my solution in the SWF file displayed below. You can right-click and select the “View Source…” option to see the implementation details.
I hope this helps you avoid chasing this issue in your future development efforts!
About this Post
Permalink | Trackback |
|
Print This Article |
Comments
5 Responses to “Proceed With Caution: Adobe Flex Inline Functions in Loops”
Leave a Reply

Tommy –
I was just reading your post about creating inline functions in Actionscript, and wanted to let you know that the behavior you’re encountering is not actually a bug in the compiler, but the behavior as defined by the ECMAScript standard. You’ll see the same behavior in Javscript, AS1, and any other implementation of ECMAScript.
When you create an inline function, the VM makes the current scope (i..e, all of the variables, parameters, globals that are visible to the current code) available to the function definition. It does _not_ copy over the values, but rather makes the actual variables and parameters available. Which means that if you change their value in the function, that change will persist from invocation to invocation. For example:
Public function makeCounter():Function
{
var count:Number= 0;
return function():void { return count++;}
}
var counter:Function = makeCounter();
trace(counter()); // “0”
trace(counter()); // “1”
trace(counter()); // “2”;
It also means that if you create multiple functions inside the same scope, they will refer to the same variables, parameters, etc. And if you modify those vars/params in the outer function, it will affect the values visible in the inner functions. All of which is a really powerful feature, but one you do have to be careful of.
There’s an easy way to trigger the behavior you specifically were looking for, which is to capture the current value in a new scope by calling a sub routine. For example:
Public function makeFuncArray():void
{
Var result:Array = [];
For(var i:int =0;i<1000;i++)
{
Result.push(makeSingleFunc(i));
}
return result;
}
Private function makeSingleFunc(index:Number):Function
{
Return function():void
{
Trace(“I am function number “ + index);
}
}
Would create 1000 functions, each with their own unique captured scope.
Hope that helps.
Ely.
Just wondering, have you tried to use Adobe Enterprise Developer support (AEDS) to give you an helping hand to understand this issue (Have read at your previous blog entry that you had opted for this program at 11/2006: http://www.tommyb.com/2006/11/30/are-you-serious-about-adobe-flex-development-you-need-aeds/
I am just currently evaluating pros and cons of joining AEDS program for my company…
Ely: Thanks so much for the clarification! That makes sense. Your solution of a separate function returning the Function reference is a simpler solution, given that no separate class is needed.
JabbyPanda: In this case, I didn’t send it to AEDS as the time between recognizing the problem source and creating a solution was short. I would have submitted a ticket on it if a solution hadn’t been obvious. I don’t plan to now that Ely has provided a clear explanation of the issue.
I would still recommend AEDS, particularly if you’re new to Flex and ECMAScript-based languages. They offer a guaranteed response time and have direct access to the Flex engineering team.
If you’re doing Flex work full-time and find yourself running into issues that haven’t already been addressed in the flexcoders list or Adobe support forums, you’ll find that AEDS pays for itself pretty quickly.
Thank you so much for that post.
Saved me a lot of frustration.
Hi Jack,
Glad to hear it helped! It was very frustrating to me at the time as well.