Archive for the ‘Flex Gotchas’ category

Type Casting and ObjectUtil.copy(obj) in ActionScript 3.0

May 7, 2010

When programming in Object Oriented Languages, in this case ActionScript 3.0, having different copies of the same object is sometimes a common need.
For many, the most obvious approach would be to simply assign one object to another, like this:

myObj2 = myObj1;

But this approach would not copy the objects. Instead this approach would only copy the object references. In the end there will be only one object, which would be referenced from two places, meaning that changing myObj1 is the same as changing myObj2 and vice-versa.
By the way, in Flex everything is a reference!

The question is how do we clone an object in ActionScript 3.0?

ActionScript 3.0 has several utility functions, and one of them is the ObjectUtil.copy(Obj) which can be found in the package mx.utils;

This function will perform a deep copy of the object that is given as argument. And this is done by using the built in flash player AMF capabilities (yes, in Flex you will find really odd and original solutions for problems). What happens here is that the entire object is serialized into an array of bytes, and when the bytes are deserialized, a brand new object is created copying all of the original contents.
Still, you won’t be able to type cast most of the resulting objects (in fact, only primitive types can be type casted). And this is because when an object is deserialized from AMF, although it haves all the properties of a class instance, it will not be a true class instance nor will hold any references to the original class.
This is solved by adding type information about the object to the AMF packet. This can be done by using the registerClassAlias() method which is available in the flash.net package. This method will allow to preserve the class (type) of an object when the object is encoded in Action Message Format (AMF).

Let’s take a look to the following source code:

public var myObj:MyObject = new MyObject();
myObj.someProperty = “myProperty”;

public var myObjCopy:Object = ObjectUtil.copy(myObj);
trace(myObj.someProperty): // “myProperty”
trace(myObjCopy.someProperty): // “myProperty”
myObjCopy.someProperty = “myChangedProperty”;
trace(myObj.someProperty); // “myProperty”
trace(myObjCopy.someProperty); // “myChangedProperty”

public var myObj:MyObject = new MyObject();
myObj.someProperty = “myProperty”;
public var myObjCopy:MyObject = ObjectUtil.copy(myObj) as MyObject;
trace(myObj.someProperty): // “myProperty”
trace(myObjCopy.someProperty);

//FAULT: cannot access object with null reference.
//Execution stops!

public var myObj:MyObject = new MyObject();
myObj.someProperty = “myProperty”;
registerClassAlias(“my.package.myObject”,MyObject);
public var myObjCopy:MyObject = ObjectUtil.copy(myObj) as MyObject;
trace(myObj.someProperty):   // “myProperty”
trace(myObjCopy.someProperty):  // “myProperty”
myObjCopy.someProperty = “myChangedProperty”;
trace(myObj.someProperty);  // “myProperty”
trace(myObjCopy.someProperty); // “myChangedProperty”

Why does it work on A and C and not on B?

Well, in A it works because we are using the type Object. And it would also for other primitive types such as Array.
In C it works because we are passing (registerClassAlias) the object type before it gets encoded, which will result in a successful type cast.
In B we don’t preserve the objects type, meaning that after the object gets deserialized it will hold no references to the MyObject class. The result will be a failed type cast which will result in pointing out to a null object reference.

But… ObjectUtil.copy(obj) it’s not bullet proof. I will not address to this situation because Darron schall has already a great post on this issue.

Advertisements