Ok. So one of the all-time most confusing things when coding in actionscript (1 or 2) is the way references are handled. Some people are a little unclear on them, some people are misinformed on them, and many don’t even know they exist. This article sets itself the ambitious task of trying to explain and clarify when flash uses references.
What are references?
One of the most potentially confusing, (yet amazingly useful and powerful) topics that people struggle with in flash is the concept of references.
References come from languages like C & C++, where the programmer deals with the computer’s memory in a more direct way – the idea of a reference is to pass the address of a value, rather than the value itself. For example: say I’ve got an array with 200 elements, and each of those 200 elements contains 2000 characters. Now imagine I have a function which will take an array as a parameter, and do something to that array (for example: shuffle the elements around). Rather than having to copy the entire array (which would probably be horribly slow and memory/cpu intensive), I can just give my function the address of the array, and let it do its thing.
A simple analogy for this concept is to consider e-mail attachments: say I’ve just written a new flash game – I can send it to someone an email attachment, or I could just send them the URL. This is what happens with references – only the address of the object is sent.
When does flash use references?
Flash uses references nearly all the time. It’s actually easier to list the times when flash doesn’t use them.
The datatypes in flash are divided into complex and primitive datatypes. The primitive data types are:
String
Number
Boolean
All the other ones – MovieClip, Array, Object, etc… are complex datatypes. These are the ones which will get passed around by reference.
Primitive datatypes generally behave pretty much the way you would expect – when they’re set, they’re set, and that’s the end of it.
var a:String,b:String;
a = "value1";
b = a;
trace("a="+a);
trace("b="+b);
b = "value2";
trace("a="+a);
trace("b="+b);
If I set ‘a’, then set ‘b’ equal to ‘a’, then set ‘b’ to something else, ‘a’ won’t change. Much as you’d expect.
However – with complex datatypes, what’s happening is different. Try this:
var arr1:Array, arr2:Array;
arr1 = ["apple","orange","pear"]
arr2 = arr1;
trace("arr1="+arr1.join(","));
trace("arr2="+arr2.join(","));
//now let's change one of the elements in arr2:
arr2[0] = "grapefruit";
//that looks fine.
trace("arr2="+arr2.join(","));
//but wait - look what's happened to arr1!
trace("arr1="+arr1.join(","));
The value of Arr1 changed when we set the first element of Arr2! What’s going on?
The truth here is that there is really only one array – with two references pointing to it. Because Array is a complex datatype, when we assign it using the ‘=’ operator, it is passed by reference, and it’s not copied – Arr2 is just a reference back to Arr1. This can cause a lot of problems if you are working with arrays – more on that later.
Likewise with MoveClips – for example:
if you create a movieclip on the stage like this:
attachMovie(”myLinkageID”,”myInstanceName”,1);
What happens is that a movieclip is created in the movieclip tree, and in the local scope a reference to its memory address will be placed in the variable myInstanceName.
[note - as you are probably aware, in flash, the sub objects of the _root movieclip are arranged in a tree structure - movieclip objects have certain inherent properties and can also contain local variables as well as other movieclips]
We can now talk to myInstanceName via the reference myInstanceName, and can access its properties and methods by using normal dot syntax, e.g. (myInstanceName.someVar = “someVal”)
However – this is where it gets interesting. What if we do this instead:
var myMcReference:MovieClip = attachMovie("myLinkageID","myInstanceName",1);
Now we have two references to the MovieClip – myInstancename, and myMcReference. Try setting a property on one and you’ll see what I mean:
myInstancename.foo = "hello, I am a movieClip"; trace(myMcReference.foo);
Will output:
“hello, I am a movieClip”
Why is this? What’s happening is that the attachMovie method is returning a reference. You now have two references to the same movieclip in memory.
This can be very useful, and also potentially very problematic, especially if you’re getting wierd behaviour and don’t know what’s happening.
References we’re familiar with
If you’ve used flash for any amount of time, you’re more than likely familiar with the most commonly known references: _parent,_root, and this.
_parent is a reference to the movieclip that contains another movieclip – it’s very useful for navigating through complicated nested movieClip trees. However, if you’ve got a complicated structure it can get very cumbersome and confusing:
_parent._parent._parent._parent._parent.myMc.gotoAndStop(”someFrame”);
This is why a lot of people just give up in despair, and attach everything on _root.
_root is a reference to the top level movieclip, the one which contains all the others. However, if you need to load your swf into another swf, or attach it to different levels, it can cause serious problems (although this has been partly addressed in v7 with the arrival of lockroot, it’s still best to not use _root if at all possible).
Another important thing to remember about the _parent reference is that it only exists in movieClips. When you create an Object() object it will not have a _parent reference. This catches nearly *everyone* out. Usually when you refer to _parent the scope chain will resolve to the right mc instance, but it’s not 100% reliable. Just be aware of it.
The third reference that you may already be familiar with is ‘this’. This is a source of much befuddlement and confusion, but when understood properly can be immensely useful…
The ‘this.’ reference
‘this.’ represents the instance that calls the method. For example, lets create a class MyCoolClass:
in MyCoolClass.as
——————–
class MyCoolClass{
private var myVar1:String;
private var myVar2:String;
public function MyCoolClass(){
myVar1 = "a string created in the class scope"//<--put the breakpoint on this line
this.myVar2 = "a string created in the 'this.' scope."
}
}
in actions panel
--------------------
import MyCoolClass; var myClass:MyCoolClass = new MyCoolClass();
If you compile this class using the flash debugger, and place a breakpoint on the first line, then press play and look at the 'locals' tab - you will see an object called 'this', with a + beside it. If you click on it, it will open up a list of variables - step through and you'll see myVar1 and myVar2 being created there. This list is a reference to the instance of movieclip that the class is attached to. The compiler will resolve your class variables at compile time, and try to automatically resolve your local vars to point towards 'this.' However, sometimes, usually around three hours before your deadline, this will somehow break down, and a function call will disappear into the ether, and you'll have no idea why. If you use the 'this.' scope consistently and sensibly, you'll have a much easier time debugging your code. You can learn a lot about what's really going on by watching the debugger. Remember - 'this' refers to the instance that owns the method - so if we have a movieclip on the stage, this._name refers directly to the movieclip's _name property. We can send a reference to a particular scope by passing 'this' as a parameter from a function or method in that scope.
Using References for Good
You can do quite a few cool things with references. One of the most powerful things is passing movieclip references. For example, say I want to be able to refer to the _root timeline, but I don't want to use _root (for the aforementioned reasons). Instead, what I could do is create a reference to it in the _global scope, which I can refer to from anywhere. For example:
//on the root timeline:
_global.myRootRef = this;
//in some other nested movieclip
_global.myRootRef.someVar = "someValue";
Now check in the debugger, and you'll see that someVar has been set on the _root timeline. You could easily use this method to set up references to the important areas in your flash movie.
Another useful thing you can do is attach a reference back to the creator object. Say I want to create multiple instances of a movieclip, which could be anywhere on the timeline, but I'd still like to be able to talk directly with the class that created them. I can pass the reference to the creator class via an init object, and still access all of the creator class methods and properties:
var initObj:Obj = [creatorRef:this];
myClipRef = _root.attachMovie("myLinkageID","myMcInstance1",1,initObj);
We now have 2 useful references: in the creator class, we have a reference to the newly created clip, and in the newly created clip, we have a reference back to the class itself. This can be useful for all sorts of reasons, as you can call the creator class functions from the new movieclip like this:
creatorRef.someCreatorClassFunction();
Passing references as parameters
You can also use a reference as a parameter in a function - which means you can apply that function anywhere, as long as it's all correctly scoped.
For example, lets say we want to create a function which can draw a grid. We could create this function and attach it to every instance where it's needed. However, it might get quite difficult to find where it was being drawn if we needed to draw several grids in different places at the same time. Much easier and more readable to be able to just go:
drawGrid(someRef, width,height); drawGrid(anotherRef, width,height); drawGrid(yetAnotherRef, width,height);
That way we can do everything at once, and call them all from the same place in the code.
So, let's see our drawGrid function:
function drawGrid(movieClipRef,gridWidth,gridHeight):Boolean{
//the drawGrid function would probably take more parameters in real life, but let's just go with width & height for now
if (movieClipRef==undefined){//first make sure that the reference is valid
return false;
}
var GRIDSIZE:Number = 16//constant that defines the grid square size
//draw horizontal lines
var hLines = Math.floor(gridWidth/GRIDSIZE);
movieClipRef.lineStyle(1,0x000000,100);
for (var i=0; i< hLines;i++){
movieClipRef.moveTo(i*GRIDSIZE,0);
movieClipRef.lineTo(i*GRIDSIZE,gridHeight);
}
var vLines = Math.floor(gridHeight/GRIDSIZE);
for (i=0; i< vLines;i++){
movieClipRef.moveTo(0,i*GRIDSIZE);
movieClipRef.lineTo(gridWidth,i*GRIDSIZE);
}
return true;
}
Because the function takes a reference as a parameter, we can apply it to any movieclip reference and it will execut the relevant code there - in this case the drawing API commands. There are a whole host of useful applications for this technique.
These References were created for Good, but they've turned to EVIL!
However - it's important not to go berserk with references, for a couple of reasons. Firstly, they are pretty much guaranteed to confuse the hell out of other people trying to read your code (unless they've read this article) and secondly, they may potentially confuse the garbage collector .
Thought that when you deleted a movieclip via 'removeMovieClip' that you were actually deleting it? Wrong! In fact what happens is that you are deleting the reference to it that links it to the stage, and marking it for garbage collection. It disappears from the screen because when flash redraws the screen, there is no longer a reference from the mc to the screen buffer. That's when the garbage collector comes into play.
Every night, (or every wednesday, depending on how much council tax you pay) the garbage truck goes down the street and looks for anything that is packaged in a big black polythene bag and sitting on the road outside a house. They automatically assume that these bags are garbage. However, if you left a large piece of furniture on the road beside a load of rubbish bags, they probably wouldn't take that away. Because it's not wrapped in polythene, it might or might not be garbage, and taking it away could cause lawsuits, complaints, or letters to MPs/MEPs/Senators (delete where appropriate). As it's better to be safe, they will normally leave it there. And this is how the garbage collector in flash works as well. When there are no references to an MC, it removes it from memory. If there are still references to it, it leaves it there. Just like the garbage men leave the furniture on the pavement.
The garbage collection in flash is pretty decent, so mostly you won't have to worry about it, unless you're creating large XML trees, in which case you should be careful to delete your references when you're finished them. I have only done a little bit of reasearch on this, and it seems that you can happily create multiple references to movieclips and delete them without a significant memory overhead (movieclips are *very* well implemented since version 3), however with other complex data types it might not be the case. A thorough testing of flash's garbage collection is a little beyond the scope of this article.
So that's about it with reference usage - except for one last thing: databinding.
Databinding is mostly done by simpy setting references between complex datatypes - pretty much the technique described above. It can be handy if you're creating your own components & classes. It basically takes advantage of the problem we went over earlier, where array1 = array2 creates a reference to the whole array. You can bind you data using the same technique. A useful thing to note is that if you set a watch on a variable in the debugger, the little blue dot will be seen on both the original AND the reference. This can be helpful when you're trying to work out if something is picking up a reference to something else.
Anyway, that's about it - feel free to point out inaccuracies, or raise questions in the comments section, I'll do my best to answer them or amend the article, (whichever is easier).