« Designing motion graphics pt3: Code | Main | The state of Web Development in 2006/2007 »
AS2.0 development gotchas
In the last few months I've been working on a number of largish AS2.0 coding tasks. I've come across a few problems/bugs that I've spent literally days debugging/rewriting.
Just so you don't have to go through the same pain, here's a quick lowdown on my top timewasting/obscure-but-common problems and issues, with a fix for each.
You need better realtime debugging tools when writing classes
The debugger panel just doesnt hack it when using the standard Flash IDE,
and trace isn't that good either because its limited in what it can do (it
simply traces values to the output panel).
Fix: You can trace function calls. This one is so easy once
you see it, but many coders have never seen it. If you are writing a class,
create a debugging method called snapShot() that itself traces
a snapshot of the relevant values/states in the class (and returns null to
the calling trace()). Thus, whenever you want to debug an error,
use
trace(snapShot());
Rather than using trace() to output text, we use it to
call a debug function. This way the snapShot() function
can do all sorts of things other than just display data (it can contain test
harness logic that forces values for example). As with all trace actions, this
trace is not exported to the final swf, so the debug code never gets invoked
in the final swf. Sweet!
Don’t assume that delete actually deletes immediately.
When you use the delete action, you are relying on the Flash
player’s memory 'garbage
collection', and this is dangerous for two reasons. Firstly, the garbage collection
routines are player specific; Flash player 8 is better at garbage collection
than Flash player 7, but how good either are is not a documented feature. Also,
even though you may delete something, the something may be forcing itself to
be kept in memory in a number of ways (such as references that are still
active, particularly if they reside in anonymous functions).
The issue of delete not deleting immediately is not normally
an issue, except when you are creating event driven code where the events occur
very quickly, (i.e. on the same frame). The fact that something you thought
you deleted is actually still there may then cause all sorts of problems, especially
if you can’t always be sure of the order of events (e.g
when your code is at least partly driven by events generated by external webservice
returns or the user, and both generate lots of graphic changes in the UI).
Fix: If you want something to be deleted immediately, make
it null first.
That way, you are much less worried if the something is not actually deleted
right away because you know its value is deleted right away.
For example, if you want an onEnterFrame to become
inactive right away, don't use:
delete this.onEnterFrame;
Consider doing this instead:
this.onEnterFrame = null;
delete this.onEnterFrame;
Another closely related issue I’ve seen a lot is where you are implicitly expecting Flash to delete local variables in event handlers, particularly those associated with recieving data. For example, Flash may not be deleting XML instances when event handlers end. The fix here is to:
- Explicitly clear local XML instances by setting them to null.
- Avoid using anonymous functions. The Flash garbage collection seems to be particularly bad with them (its better in Flash 8 and later, but how much better is not a documented feature, so don’t count on it). The Flash 7 player (and previous versions) are notorious for memory leakage, especially when you are building web applications. In the testing I've done, this seems to be largely due to XML instances not clearing themselves from memory.
Following the fixes seen above makes the Flash player much more stable when there's a lot of communications with a back-end via XML/webservices going on, especially for Flash 7 and previous versions of the player (and although we have Flash player 8 and 9 at the time of this writing, most projects still specify compatibility with Flash player 7 as a requirement).
Avoid using this in an AS2.0 class because scope bugs are hell to fix.
Okay, I know some people disagree totally with this, but I’ve found time
and time again that using this in a class is a major timewaster
when it comes to debugging. The problem here is that unlike other languages,
in ActionScript, this can very easily change in a class
containing event handlers. It can be difficult to see that this has
changed if you have a scoping bug because this will always have
a valid value, whereas lots of other things will become invalid (such as
all the class properties because you are no longer scoping the class). The
biggest problem in fixing this bug is that the situation causing the bug
(an incorrectly scoped event handler) isn't persistent - once the event
handler finishes, the properties magically become defined.
You can (and always should!) prevent
this from changing throughout a class by using Delegate.create() when
defining event handlers, but the problem is when you have bugs; if you forget
or fail to force scope to remain unchanged, this is still equal
to something (it usually becomes scoped to a property that can have event
handlers, such as a movie clip or XML instance, rather than to the class).
It would be far better if this became invalid,
because that’s
easier to debug.
Fix: Instead of using this directly, use a property that is equal to this:
// In property definition…
private var me:MovieClip;
...
...
// In constructor…
me = this;
The cool thing here is that property me will be undefined if
you ever go out of class scope, thus making scoping errors quicker
to pick out when debugging (because code that doesn't work is easier to fix than code that works but does the wrong thing). I can’t emphasize how much time you will save
by doing this; most timewaster bugs in class based code are to do with scoping
error.
A more structured solution is to ensure that any property that has event
handlers is coded up via its own class. That way, this in the event hander
and this in the class are actually the same thing. My feeling here is that you are then (at least partly) working around the problem rather than going head-on at the issue and fixing it; you should only be creating new classes if your model requires it, and not to work-around Flash.
It should of course be noted that the problem is less important in AS3.0... but AS3.0 is still beta and it will be at least the middle of next year before it bcomes mainstream.
Class inheritance doesn’t know about streaming.
Class inheritance works well when you are creating components because registering classes to movie clips causes the classes to load at the same time as the movie clips. You can also define which frame classes that are registered to movie clips load (File > Publish Settings > Flash Tab > ActionScript Version: Settings).
Class inheritence doesn’t always work when you have classes that are simply
datatypes (i.e. they are not registered to a movie clip). In this situation,
a subclass may load before its superclass, which is very bad for your code; super() stops
working because the superclass is not yet available. Worse still, Flash won’t
tell you the error occurs. Even worse still, you probably won’t
even see the error until you put the application online!
Fix: The simple (and obvious) solution is to fully preload your application before running any class based code. The problem is that this is not always possible, particularly for more complex applications. A better way is to explicitly force classes to load at a certain frame ('better' because it sounds like a fix rather than a structural workaround!). You can do this by calling a static class method on the required frame. By using a static method, you can force the class to load before you start creating instances.
Instances don't contain methods and properties; they contain data.
Okay. This is not really a bug but a way of thinking when writing AS2.0 code. All reference material you read on OOP tells you about classes >> methods >> properties. Thus, when you start writing classes, your mindset in centered around methods and properties, and you may fall into the trap of seeing classes as defining 'containers of logically related code'.
Classes define containers of data. This data is held internal to each instance (or static class), and methods and properties are merely the external interfaces to that data. This is an important mindset to work to when defining your classes, but its always easy to forget...
Thus when defining a class, think first in terms of what data each instance should contain, and then think about the inerfaces external code will need to access that data. What you should not do is simply think about what methods and properties the instance should have, then backfit the data to support it. You can tell if you have done the latter, because a couple of months down the line, your classes will start to seem unwieldy and more trouble maintaining than they are worth!
Posted by motiongraphics on October 3, 2006 12:34 AM
Wow! Couldnt understand a word of it! But it sounds so good... No wonder I am a sound guy!
How do people like you understand AS so well. AS 3 is out and I am still not really using AS 2. AS 1 doesnt even exist... I mean it was never called AS 1 when it came out. I wish I could write code that needed debugging!
Posted on October 29, 2006 04:00 PM
Hi Walmik.
I think the biggest thing to realise about code is that it controls data, not graphics... but of course, in Flash, that data can also have a visual appearance.
Nevertheless, the trick with code is to see the effects you want to create in terms of data. For example, if you want to make something move from left to right on the screen, you need to think in terms of the x co-ordinate of the 'something' increasing in value.
Perhaps the quickest way to learn to code is to be near other coders. I remember one contractor who had to sit in our office for a month, surrounded by us (five Flash developers). He came in as a tween Flash animator. When he left, he was starting to write class based AS2.0.
I told him to be sure to increase his hourly rate on his next contract :D
Posted on October 30, 2006 10:27 PM
There's an interesting set of blog entries about Flash garbage collection (which relates to the issues with the delete keyword) at
http://www.gskinner.com/blog/archives/2006/06/as3_resource_ma.html
Posted on January 4, 2007 10:11 AM
Hi Sham,
I had totally forgotten that I had commented here! Its so nice of you to have suggested that tip about thinking in terms of code controlling data and not graphics. It opens a whole new way of looking at coding. It just makes it seem so logical!
About sitting with other coders to improve or learn, well that is not possible for many people working from home in India and hence the learning is slow. I was wondering if you could write something about 'logic' in coding and learning on your own, not necessarily for any particular language, but on the whole. That, i think, would be a great help for people who work alone and depend on posts by accomplished people like yourself.
Posted on February 20, 2007 08:58 AM
this is a bit off topic, but do you know if there is a way to trace information about an object like what properties and methods it has? if you just trace the name of the object you get something like [object Object] which doesn't tell you much...
b r e n t
Posted on August 29, 2007 03:19 PM
wait I think this is what you were writing about with trace(snapshot()) but where is the function for snapshot()?
Posted on August 29, 2007 03:21 PM