I have been having a great time using ActionScript 3.0 and Flex but
I have encountered problems which seem to be caused by Flexbuilder
or the Flashplayer runtime. The following problem is based on a
situation I have in my application uses a data structure of embedded
objects. I replicated the problem using arrays instead of the
classes I had used. The example may seemed contrived but reproduces
the problem without duplicating the complexity of my application.
The structure was based on data structure of page objects, which
contains several cell items. Each item has a list of animations,
and each animation has a list trigger events references. So the
nested structure looked like the following:
Pages (instance 1)
Item (instance 1)
Animation (instance 1)
Events (instance 1)
Events (instance 2)
Animation (instance 2)
Events (instance 1)
Events (instance 2)
Item (instance 2)
Animation (instance 1)
Events (instance 1)
Events (instance 2)
Animation (instance 2)
Events (instance 1)
Events (instance 2)
The setup function in my example creates this structure using
arrays. The function "goodResult" traverses the structure using
nested for-loops. Nested local variables at different scoping
levels are referenced and displayed during the inner most loop for
the Events (instance 1 & 2). This produces the expected result.
Good Results
500 200 [v:0 i:0 a: 0 e: 0]
500 200 [v:0 i:0 a: 0 e: 1]
500 200 [v:0 i:0 a: 1 e: 0]
500 200 [v:0 i:0 a: 1 e: 1]
500 200 [v:0 i:1 a: 0 e: 0]
500 200 [v:0 i:1 a: 0 e: 1]
500 200 [v:0 i:1 a: 1 e: 0]
500 200 [v:0 i:1 a: 1 e: 1]
The "badResult" function traverses the structure using the OO
technique of enumeration (iterator) functions. Among other reasons,
I used this approach to make the code more readable and compact by
hiding the loop from the caller and the typecasting the values
automatically. The caller is required to send a Function which is
then called during each pass (iterator) ofthe loop. The nested
local variables this time are not displayed properly. During the
first call, the results for Events (instance 1) is correct, but
Events (instance 2) is incorrect. Subsequent calls to
the "badResult" function display erroneous results.
Bad Results - first iteration - first event loop good, second loop
bad
500 200 [v:0 i:0 a: 0 e: 0]
500 200 [v:0 i:0 a: 0 e: 1]
500 200 [v:0 i:0 a: 1 e: 0]
500 200 [v:0 i:0 a: 1 e: 1]
500 59369881 [v:0 i:1 a: 0 e: 0]
500 59369881 [v:0 i:1 a: 0 e: 1]
500 59369881 [v:0 i:1 a: 1 e: 0]
500 59369881 [v:0 i:1 a: 1 e: 1]
Bad Results - second iteration - both loops bad
2 500 [v:0 i:0 a: 0 e: 0]
2 500 [v:0 i:0 a: 0 e: 1]
2 500 [v:0 i:0 a: 1 e: 0]
2 500 [v:0 i:0 a: 1 e: 1]
2 500 [v:0 i:1 a: 0 e: 0]
2 500 [v:0 i:1 a: 0 e: 1]
2 500 [v:0 i:1 a: 1 e: 0]
2 500 [v:0 i:1 a: 1 e: 1]
Bad Results - third iteration - both loops bad
2 500 [v:0 i:0 a: 0 e: 0]
2 500 [v:0 i:0 a: 0 e: 1]
2 500 [v:0 i:0 a: 1 e: 0]
2 500 [v:0 i:0 a: 1 e: 1]
2 500 [v:0 i:1 a: 0 e: 0]
2 500 [v:0 i:1 a: 0 e: 1]
2 500 [v:0 i:1 a: 1 e: 0]
2 500 [v:0 i:1 a: 1 e: 1]
I don't know if the problem is with the enumeration function calls
or stack references during nested scope (this is not recursion so
the stack depth should not be that great). The example is simple so
you can play around with it. It does make me worry about using
Functions or nested function calls.
Here is the code for the example.
Regards,
Rod Perkins
---------------------------
package
{
import flash.display.Sprite;
import flash.events.Event;
public class BuildError_2 extends Sprite
{
private var _pages : Array = new Array();
public function enumerateEvents( _events : Array, func :
Function ) : void
{
for ( var x : int = 0, numItems : int =
_events.length; x < _events.length; x++)
{
var eventObj : Object = Object(
_events[x] );
if (func.call( this, x, numItems,
eventObj ) == false) break;
}
}
public function enumerateAnimations( _anim : Array, func :
Function ) : void
{
for ( var x : int = 0, numItems : int =
_anim.length; x < _anim.length; x++)
{
var animObj : Object = Object( _anim
[x] );
if (func.call( this, x, numItems,
animObj ) == false) break;
}
}
public function enumerateItems( _items : Array,
func : Function ) : void
{
for ( var x : int = 0, numItems : int =
_items.length; x < numItems; x++)
{
var itemObj : Object = _items[x];
if (func.call( this, x, numItems,
itemObj ) == false) break;
}
}
public function setup() : void
{
_pages = new Array();
for ( var pageNo : int = 0; pageNo < 1;
pageNo++)
{
var _items : Array = new
Array();
for ( var itemNo : int = 0;
itemNo < 2; itemNo++)
{
var _anim : Array =
new Array();
for ( var aItemNo :
int = 0; aItemNo < 2; aItemNo++)
{
var
_events : Array = new Array();
for ( var
eItemNo : int = 0; eItemNo < 2; eItemNo++)
{
_events.push(
"[v:" + vItemNo + " i:" + itemNo + " a: " + aItemNo + " e: "
+ eItemNo + "]"
);
}
_anim.push(
_events );
}
_items.push( _anim );
}
_pages.push( _items );
}
}
public function verifyContainer( container :
Array ) : void
{
enumerateItems
(
container,
function myContainerFunction(
itemNo : int, numItems : int, items : Array ) : Boolean
{
var itemCount : int = 500;
enumerateAnimations
(
items,
function
myAnimationFunction( animNo : int, numItems : int, anim : Array ) :
void
{
var
animationDuration : int = 200;
enumerateEvents
(
anim,
function myEventFunction( eventNo : int, numItems : int,
event : Object ) : void
{
trace( itemCount + " " + animationDuration + " " + event);
}
);
}
);
return true;
}
);
}
public function goodResults() : void
{
for ( var pageNo : int = 0; pageNo <
_pages.length; pageNo++)
{
var _items : Array = _pages[
pageNo ] as Array;
for ( var itemNo : int = 0; itemNo <
_items.length; itemNo++)
{
var itemCount : int = 500;
var _anim : Array = _items[
itemNo ] as Array;
for ( var aItemNo : int = 0;
aItemNo < _anim.length; aItemNo++)
{
var
animationDuration : int = 200;
var _events : Array
= _anim[ aItemNo ] as Array;
for ( var eItemNo :
int = 0; eItemNo < _events.length; eItemNo++)
{
trace(
itemCount + " " + animationDuration + " " + _events[eItemNo] );
}
}
}
}
}
public function badResults() : void
{
for ( var index : int = 0; index <
_pages.length; index++)
{
var container : Array = _pages
[index] as Array;
verifyContainer( container );
}
}
public function BuildError_2()
{
setup();
trace("Good Results" );
// this path will print good results
// 500 200
// 500 200
// 500 200
// 500 200
// 500 200
// 500 200
// 500 200
// 500 200
goodResults();
trace("Bad Results - first iteration - first
event loop good, second loop bad" );
// this path will print bad results
// 500 200
// 500 200
// 500 200
// 500 200
// 500 59435417
// 500 59435417
// 500 59435417
// 500 59435417
badResults();
trace("Bad Results - second iteration - both
loops bad" );
// this path will print worse results
traversing the same structure
// 2 500
// 2 500
// 2 500
// 2 500
// 2 500
// 2 500
// 2 500
// 2 500
badResults();
trace("Bad Results - third iteration - both
loops bad" );
// this path will print the same results as
the second iteration
// 2 500
// 2 500
// 2 500
// 2 500
// 2 500
// 2 500
// 2 500
// 2 500
badResults();
}
}
}
Rod Perkins
Principle Architect & Designer
Yahoo! Inc.
701 First Avenue
Sunnyvale, CA 94089
408.349.2766 (tel)
409.349.7170 (fax)
[EMAIL PROTECTED]