Case Statements
The lowly switch statement and its attendant case statements is a basic element of most C-style languages. Still, I was surprised by it recently when it seemingly ate one of my functions. Read on to see how.
I’d like you to consider the following demo app:
public function foo() { function f(): void { } for (var i:int = 0; i < 2; ++i) { switch (i) { case 0: function f2(): void { } printFunctions(f, f2); break; case 1: { function f3(): void { } printFunctions(f, f3); break; } } } } private function printFunctions(one:Function, two:Function): void { trace("one: " + one + ", two: " + two); }
The app simply runs a switch statement twice. Each time it goes into a different case. Both cases do the same thing: they call a simple function and pass two Function variables. Both cases pass the same function for the first argument but then differ on the second argument. The first case passes a dynamic function defined directly in the case. The second case passes a dynamic function created in a block in the case. Is there a difference? Let’s see:
one: function Function() {}, two: null one: function Function() {}, two: function Function() {}
When I stumbled upon this error, I had essentially written the first case. Boy was I shocked to see a null! Since AS3 uses hoisting to essentially pull variables out to the function level, it would seem to me at first that it wouldn’t matter where the local function was defined. I could also see the function being invalid after the switch, but this is usage immediately after the function is defined. If the function isn’t valid there, where is it valid? Let’s try one more experiment by adding a third case:
case 2: function f4(): void { } log("f4: " + f4); break;
And the output:
f4: null
There you have it. The function you’ve just written is totally unusable. There were no compiler errors or warnings. To me, that calls for an error or at least a strongly-worded warning. Since you will get none from MXMLC, consider this article your unofficial warning. Beware the case statement!
#1 by Roger Clark on October 4th, 2009 ·
This is just a personal opinion, but …
But comming from a C,C++ background I’ve always been very wary about the ActionScript compiler and its scoping vagueries, in AS2 and AS3.
In AS2 on the timeline, I’ve seen numerous projects where bugs occuered due to vars or functions going out of scope depening on which frame the taget function was on and which frame the current execution was on.
There did’nt seem to be a pattern to this, as sometimes code would work and in another project, similar code would fail.
I generally don’t use functions inside other functions. I know that putting functions inside other functions etc, does give some scoping protection of the function i.e. it should not be visible outside the current scope.
But you’ve demonstrated a big downside to this..
#2 by jackson on October 4th, 2009 ·
I also come from a C/C++ background and have been wary of some of AS3’s fancier features. I endeavor to use them as they do have benefits (eg. the scoping protection you point out), but often do hit potholes and find gotchas. That’s one of the major purposes of this blog: to clear the road for all of you. :) Thanks for commenting!
#3 by Roger Clark on October 5th, 2009 ·
As a followup to the switch / case scoping I’ve just come across another rather interesting scoping example that someone posted to ActionScript.org with a question about why the code works for some of the time.
(I’ve repackaged it, but its bascially the same code)
placing the enterframe handler inside the particle() func seems to almost achieve the particle() func being a class with xPos,yPos,xVel etc as properties.
Keep clicking on the stage, after a while it packs up. But I’m kinda surprised it worked in the first place ;-)
#4 by jackson on October 5th, 2009 ·
I cleaned up your post a bit by wrapping it in code formatting:
<pre lang="actionscript3">
// code
</pre>
You’ve stumbled upon the wonderful world of closures. The eFrame function has the local variables in the particle() function “closed”, meaning they are bound to it like members of a class are bound to the object by this. You may even notice that if you use this explicitly within the eFrame function that you’ll be referring to the eFrame function itself, not the instance of the Main class that you would be referring to if you used this from the particle() function.
Closures can be tough to wrap your head around, but they’re really powerful once you manage it. See these articles for some of that power:
Curry Functions
Fun With Dynamic Functions
#5 by Roger Clark on October 5th, 2009 ·
Hi Jackson.
Thanks for the link to closures.
The interesting thing about the code is that the closures do not fully work.
If you change the number of “particles” that are created e.g.
and also slow their possible movement
it doesn’t work at all.
If you create 200 (and have slow movement) the particles start comming out from the mouse click position, but usually stop after about 1 second.
I suspect that although the vars ball, xVel and yVel are initially maintained because of closure, that the Flash garbage collector is eventually deleting them.
#6 by jackson on October 5th, 2009 ·
The issue here is with your event listener. When you add it, you add it as a weak listener by explicitly passing true for the last parameter. Consider how the references are working here:
But the reference of the eFrame function by the ball is weak, so it requires another reference in order to not get removed by the garbage collector on its next pass. You see this as 1 second, but it could be whenever the GC wants to do the collection. Since you have no other references to the eFrame function, it is weakly reachable and is therefore eligible for collection. When this happens, the eFrame function stops getting called. This causes the position to never get updated.
To avoid this problem in the future, do not add dynamic event listener functions as a weak reference unless you are sure you have another reference to them. Since you usually don’t, just make them a strong reference and call removeEventListener later.
Hope this helps clear things up.
#7 by Roger Clark on October 5th, 2009 ·
Hi Jackson,
Thanks for clearing that up. I’d have never spotted the weak reference (as I didn’t write the code in the first place).
But it just goes to show how important the strength of the linkage is.
Cheers
Roger Clark