Copying Objects in AS3, part2

So, last time we looked at how to run an objects constructor to create a duplicate – which is what Adobe recommends as a replacement for duplicateMovieClip.

The problem is that even tho this solves a majority of the situations it does not let us copy an Objects state (such as a Display Objects position, graphics property or the content of an Array).

Flash copies primitive data types such as String and Int when they are assigned to a variable:

1
2
3
4
5
var one:String = "hello";
var two:String = one;
 
one = "bye";
trace(two); // "hello" (and not "bye")

However, when you assign a complex data type, such as a Vector, what you really get is a reference:

1
2
3
4
5
6
7
8
9
10
var one:Vector. <string> = new Vector.<string>()
 
one.push("something");
 
one.push("something else");
 
var two:Vector.<string> = one;
 
one.push("and another thing");
trace(two); // something, something else, and another thing

How do we get around this? We can’t get a copy of the Vector by running it’s constructor; all we would get is another blank Vector.

Copying Objects with .clone()

A common way of enabling copying on objects is to implement a clone() method on the object in question, making sure that said method writes the current objects states on a new one.

Several classes have this built in, for example: Most Events, Bitmap Data, Filters and some Components.

Copy with ObjectUtil.copy()

If you are using Flex, you have the option of using ObjectUtil’s method copy() ( mx.utils.ObjectUtils.copy() ), which returns a reference to a copy of the supplied object.

It does this by essentially copying the Object at byte-level. This is not the most efficient way to copy something, and thus it takes up some resources,

as the Object first have to be deserialized to be written to a Byte Array. Adobe has this to say:

This method is designed for copying data objects, such as elements of a collection. It is not intended for copying a UIComponent

object, such as a TextInput control. If you want to create copies of specific UIComponent objects, you can create a subclass of the component and

implement a clone() method, or other method to perform the copy.

So in other words, it’s not a silver bullet.

Copy with ByteArray.readObject / writeObject

This is a pretty sure fire way to copy something, even a deep matrix (many layered array) can be copied this way. However this method is slow, and it does not work on Display Objects. Let’s try it on our Vector:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var one:Vector.<string> = new Vector.<string>()
 
one.push("something");
 
one.push("something else");
var btArray:ByteArray = new ByteArray();
 
btArray.writeObject( one );
 
btArray.position = 0;
 
var two:String
 
two = btArray.readObject() as String;
 
one.push("and another thing");
 
trace(two); // something, something else

The problem is that when we serialize an object and write it down in byte code we loose all meta data, such as type information. So we need to know what kind of class the copyed object belongs to and coarse it. This is not a problem here, but if the Object had been deeply clustered we would have lost a lot of the time saved by this on re-setting all the associated classes within the Object. How to solve this problem is discussed by Daron Schall here

Copy DisplayObjects with Senoculars duplicateDisplayObject class

Since there is no built-in way in Flash to copy a DisplayObject with states preserved we turn to a custom class written by famous-among-flash-developers

Senocular of kirupa.com who has written the duplicateDisplayObject class:

http://www.kirupa.com/forum/showpost.php?p=1939827&postcount=172

Essentially it does things the hard way; it uses the constructor property that I discussed in my last article to make a blank copy of an item of the same class and then assigns properties of the original object to the new one (filters, eventListeners and so on).

This means that the class will not work for custom extensions of Display Objects without a small rewrite. Nor can it handle deep copying, for that you will need to run it on every reference to a Display Object inside the one you are duplicating.