How to create classed stage objects with an empty library
I was looking at an ASV decompile of a very simple class yesterday and I realised the significance of the fact that every class is associated with a library symbol. Previously I had seen that ASV displayed classes like this, but it didn't hit me. The realisation is that every class has a corresponding library symbol with a predictable linkage name derived from the full class name.
The beauty of this is that you don't need to add a symbol to your library in order to include a component; you can just use the class and the component will follow. This is a big step to free class code from being too reliant on FLAs.
So what's the secret? Well, every class is compiled into a swf in the same way that we used to include AS1 classes, by putting them inside an #initclip block of a linked movieclip. The linkage name of the movieclip is "__Packages." followed by the full package and class name. For example the class com.peterjoel.data.XMLLoader would be exported with a movieclip with linkage "__Packages.com.peterjoel.data.XMLLoader"
If you are making a component, you can define it like this:
class com.peterjoel.MyComponent extends UIComponent
{
static var symbolName:String = "__Packages.com.peterjoel.MyComponent";
static var symbolOwner:Object = MyComponent;
// associate the symbol to the class when the class
is defined
static var symbolLinked = Object.registerClass(symbolName,
symbolOwner);
}
This will allow you to use the V2 method createClassObject, without worrying about the symbol. But you could do that anyway if you were using createClassObject because that assumes that you have included the V2 framework, so you would have the export symbol for UIObject available to "borrow" anyway.
But, using this linkage, you can just use attachMovie, without relying on the framework at all. For example, define a simple class that draws a coloured rectangle:
class com.peterjoel.shapes.Rectangle extends MovieClip{
static var symbolName:String = "__Packages.com.peterjoel.shapes.Rectangle";
static var symbolOwner:Function = Rectangle;
public var width:Number = 100;
public var height:Number = 100;
public var color:Number = 0;
function Rectangle (){
draw();
}
function draw():Void{
beginFill(color);
moveTo(0,0);
lineTo(width,
0);
lineTo(width,
height);
lineTo(0,
height);
lineTo(0,
0);
endFill();
}
// associate the symbol to the
class when the class is defined
static var symbolLinked = Object.registerClass(symbolName,
symbolOwner);
}
And use it like this:
import com.peterjoel.shapes.Rectangle;
var initObj:Object = {width:150, height:80, color:0xFF0000};
attachMovie(Rectangle.symbolName, "rect_mc", 1, initObj);
Magic! :)
AS2 class size limit
There is a limit on the compiled size of an ActionScript class, as was discussed today on FlashCoders. The uncompressed limit is around 32KB.
First of all, why is there a limit? Well, since forever, there has been a
limit on how much code you can put in a single if block,
which also applies to code in loops. This is because the way that Flash compiles
these blocks is by storing how many bytes of ActionScript to skip if a condition
is met. It's a bit like a game of snakes and ladders; the interpreter goes
across the board, executing each bytecode action. When it reaches a snake or
a ladder, it travels along it and misses out a load of squares.
According to the SWF specs, the number that determines how many bytes
to skip is a signed 16 bit integer, which means it may range from -32767 to
32767 (negative goes backwards). It's just a practical consequence of a binary
format; everything has a limit.
It's not very common to intentionally construct an if block
that is so long, but every time you use a class, it checks to see if the class
has already been defined. Here is a quick example:
// AS2 class
class com.peterjoel.Example {
function method1(){
}
}
// equivalent AS1 code (courtesy of ASV)
if (!_global.com) {
_global.com = new Object ();
}
if (!_global.com.peterjoel) {
_global.com.peterjoel = new Object ();
}
if (!_global.com.peterjoel.Example) {
// the remainder of the class definition goes here
var _local1 = function () {
}
com.peterjoel.Example = _local1;
var _local2 = _local1.prototype;
_local2.method1 = function () {
}
(ASSetPropFlags(com.peterjoel.Example.prototype, null,
1));
}
The reason for the conditions is so that the class doesn't overwite another
copy of the same class that was put there by another loaded movie. If it was
to over-write it, amongst other things, the instanceof operator
would break and static variables would be lost. So it is necessary.
The most helpful information, practically, is how much code can you put in a class. Well that is very hard to quantify because some code can be very dense and other code can be bery sparse. I did a couple of very quick tests and estimate that you would need in the order of 4000 lines of code to do this. Of course it really does depend on the code itself and if you are doing complicated things on one line and how you space out your code.
But practically speaking, this is the first real life case I have heard of, and I would suggest that normally you wouldn't want to design classes that are so large anyway. I tend to get uneasy if a class starts approaching 1000 lines; and that is with my usual generous lashings of whitespace. Long classes are harder to read and maintain, and it is a sign that you might be trying to do too much in a single class. This could easily happen when you keep adding unexpected features, but it may be a good time to stand back and reconsider your design and do a bit of refactoring. There were a few suggestions in the FlashCoders thread for how you might go about this process.
Some Resources:
Refactoring:
Improving the Design of Existing Code by Martin
Fowler comes highly recommended and is next on my tech book list.
Burak Kalayci explains
the bytecode size issue in the ASV online help.
Some more info at the Flasm site.
SWF
file format
Flash Virtual Machine and SWF extensions
A few people have recently been talking about using the terminology FVM or FRE, in preference to Flash Player, when talking about it in the contect of an application, to help dispell the perception that Flash is just an animation tool. (Steve Webster in particular)
So then I started thinking about the SWFs themselves and realised that we don't even need to call them that. .swf is a Flash movie, but a Flash application could be given a completely different extension, such as .fap. And why stop there? You could use the .fv2 extension to identify that an application depends on the V2 framework, not included in the file itself, and the .rsl extension to indicate a Runtime Shared Library.
Any thoughts?
Wanted: component bugfixes
I know that a lot of developers have found and fixed bugs in the Flash MX 2004 UI components and supporting classes, so I am doing a roundup of all the fixes and I am going to put them into a Flash extension that you can all have. The cool thing about doing it this way is that you will be able to simply uninstall the extension if anything in it causes problems.
If you have a fix, please send the AS file(s) you have modified to componentchallenge@peterjoel.co.uk. Make sure the lines you have changed are clearly commented so I can merge the files easily and make sure that fixes do not conflict. Also write a few words to explain what the bug was. All contributers will be properly credited.
File Access API
Mike Chambers has asked for people's thoughts on what an API for accessing local files should look like, and what features people would like to see. He was specifically talking about Central, but of course it would be great if it was in the Flash Player too.
Here is how I would envisage a local file access API could be built, that allows the developer maximum freedom, while minimising risk to the user.
Radio button databinding component
If you use databinding, then you probably noticed that radio buttons do not have any bindable properties and and radio groups are not even on-stage enitities, so do not support binding either. So I made the and RadioGroupBinder component, which acts as a kind of proxy to your radio group and lets you bind to it.
Put the component on your stage, in the same timeline as your radio buttons and set its groupName property to the same as you gave to your radio buttons. Then you can use the data binding panels to bind to its selectedIndex and value properties.
update: Marc Vezina sent me an FLA of a simple quiz, which uses the RadioGroupBinder component and Konrad Wulf's Quiz Web Service.
