[AS3] Pass by value

12 posts

Flag Post

Hey guys, I need to pass an object by value. With arrays, you can just use .splice(), is there anything equal to objects, or do I have to make a function myself?

 
Flag Post

Whenever you pass an object, it always passes the reference (In AS3 anyway). However, it is possible to make a “copy” of the object, which is essentially what your after, by writing the object into a byte-array.

public function clone(source:Object):*
{
	var ba:ByteArray = new ByteArray();
	ba.writeObject(source);
	ba.position = 0;
	return ba.readObject();
}

So if you had something like

public function newWaffle():void
{
	var waffle:Waffle = new Waffle();
	waffle.tasty = true;
	var waffle2:Waffle = clone(waffle);
	waffle2.tasty = false;
	
	//waffle1.tasty = true;
	//waffle2.tasty = false
}

Whereas simply making waffle2=waffle would mean when you change waffle2.tasty=false, then waffle.tasty would also equal false.

EDIT: So, if you wanted to ultimately pass an object by value to a function, you would do this

public function blahblah():void
{
	var item:* = //whatever your obj is
	doFunction(clone(item)); //Essentially you have passed the object by value.
}
 
Flag Post
Originally posted by DPbrad:

Whenever you pass an object, it always passes the reference (In AS3 anyway). However, it is possible to make a “copy” of the object, which is essentially what your after, by writing the object into a byte-array.

public function clone(source:Object):*
{
	var ba:ByteArray = new ByteArray();
	ba.writeObject(source);
	ba.position = 0;
	return ba.readObject();
}

So if you had something like

public function newWaffle():void
{
	var waffle:Waffle = new Waffle();
	waffle.tasty = true;
	var waffle2:Waffle = clone(waffle);
	waffle2.tasty = false;
	
	//waffle1.tasty = true;
	//waffle2.tasty = false
}

Whereas simply making waffle2=waffle would mean when you change waffle2.tasty=false, then waffle.tasty would also equal false.

EDIT: So, if you wanted to ultimately pass an object by value to a function, you would do this

public function blahblah():void
{
	var item:* = //whatever your obj is
	doFunction(clone(item)); //Essentially you have passed the object by value.
}

Actually, I believe you can just use .concat()

Any primitive type (int, number, string, uint, etc) passes by value. This means when it is given to a function, the function creates a new value equal to that value. That way when you do this:

private var coins:int = 12;
public function doSomething(c:int):void{
    c += 5;
    // This would change the value you passed, not the 'coins' variable.
}

HOWEVER, any object passes by reference:

private var obj:Object = new Object();
obj.randomVar = 12023;
doSomething(obj);

public function doSomething(o:Object):void{
    o.randomVar = 31415;

    trace(obj.randomVar); // Old variable
    trace(o.randomVar); // New variable
}

When you trace them, they are both 31415. This is because variable ‘o’ is technically a reference to the variable ‘obj’, its not necessarily its own variable.

You can fix this by calling .concat(), which cuts the reference and makes a clone.

 
Flag Post

Make the function yourself: there is no clone() method as there is for BitmapData, and I think there’s a good reason for it: not only would it be little needed but you can probably do far better copying the object yourself, knowing what data needs to be copied, what needs to be modified when copied, which if any referenced properties also need cloning, what can be ignored or will be initialised by the default constructor and doesn’t need reinitialising, and so on.

 
Flag Post

concat() is for Vectors/Arrays.

About the generic clone, you could also get the class name, create a new instance of that class and the copy the values of all the properties (either by [ ] access or via XML), but as it’s been suggested, you’d be better creating custom methods for each object, since that would be way faster and give you more control.

Also, random fact: primitives are also passed by reference (because of performance), but they’re immutable objects, which gives the ilusion of passing by value.

 
Flag Post
Originally posted by RTL_Shadow:

When you trace them, they are both 31415. This is because variable ‘o’ is technically a reference to the variable ‘obj’, its not necessarily its own variable.

Strictly speaking, o isn’t a reference to obj. Both o and obj are a reference to the same object in memory.

Additional random fact: any constant string is actually a reference to the same string in memory. For example:


var a:String = “hello world”
var b:String = “hello world”

Both a and b reference the same string.

 
Flag Post

Woah, all that comments, I’ll see what I can get out of it. Thanks guys. As RTL_Shadow understood, I’m talking about the object datatype (often called libraries in other languages), not random objects. The object may contain other objects, arrays or other datatypes, so a general function would be great, if this is not possible, I think I must do a dirty “hack” :P

 
Flag Post

The general function is the dirty hack.

 
Flag Post

All right, sounds like I got no choice then ^^ Thanks for the help.

 
Flag Post
Originally posted by RTL_Shadow:
Originally posted by DPbrad:

Whenever you pass an object, it always passes the reference (In AS3 anyway). However, it is possible to make a “copy” of the object, which is essentially what your after, by writing the object into a byte-array.

public function clone(source:Object):*
{
	var ba:ByteArray = new ByteArray();
	ba.writeObject(source);
	ba.position = 0;
	return ba.readObject();
}

So if you had something like

public function newWaffle():void
{
	var waffle:Waffle = new Waffle();
	waffle.tasty = true;
	var waffle2:Waffle = clone(waffle);
	waffle2.tasty = false;
	
	//waffle1.tasty = true;
	//waffle2.tasty = false
}

Whereas simply making waffle2=waffle would mean when you change waffle2.tasty=false, then waffle.tasty would also equal false.

EDIT: So, if you wanted to ultimately pass an object by value to a function, you would do this

public function blahblah():void
{
	var item:* = //whatever your obj is
	doFunction(clone(item)); //Essentially you have passed the object by value.
}

Actually, I believe you can just use .concat()

Any primitive type (int, number, string, uint, etc) passes by value. This means when it is given to a function, the function creates a new value equal to that value. That way when you do this:

private var coins:int = 12;
public function doSomething(c:int):void{
    c += 5;
    // This would change the value you passed, not the 'coins' variable.
}

HOWEVER, any object passes by reference:

private var obj:Object = new Object();
obj.randomVar = 12023;
doSomething(obj);

public function doSomething(o:Object):void{
    o.randomVar = 31415;

    trace(obj.randomVar); // Old variable
    trace(o.randomVar); // New variable
}

When you trace them, they are both 31415. This is because variable ‘o’ is technically a reference to the variable ‘obj’, its not necessarily its own variable.

You can fix this by calling .concat(), which cuts the reference and makes a clone.

Concat only works for Vectors and Arrays IIRC. Using a generic-object bytearray clone method works for anything else which isnt already a primitive class such as int/uint/String/etc.

EDIT: Also, using concat() for vectors and arrays only creates a shallow-copy vector/array, so if you had an array of arrays or vector of vectors for example, then the actual objects and values inside the 2nd array would still be hard-referenced to the originals. Using my method creates a deep-copy where every reference is removed.

EDIT2: And still, if you are actually talking about the Object datatype, then my method works fine for that as well.

 
Flag Post

In my opinion, any object you want to clone should have some kind of clone method implemented. I have an interface like this:

package {
	public interface ICloneable {
		function clone():ICloneable
	}
}

And then any class that’s cloneable implements that, for example:

package {
	public class Foo extends Object implements ICloneable {
		public var bar:String
		public function clone():ICloneable {
			var f:Foo = new Foo()
			f.bar = bar
			return f
		}
	}
}
var a:Foo = new Foo()
a.bar = "bar"
var b:Foo = a.clone() as Foo
trace(b.bar) //bar

The interface isn’t necessary, but it would allow you to do something like this:

if(someObject is ICloneable) {}

I’m not in love with it though because it requires extra casting; I wish AS had generics support beyond Vector, but c’est la vie.

The just of this whole post is to implement a clone method.

 
Flag Post
Originally posted by DPbrad:

Using a generic-object bytearray clone method works for anything else which isnt already a primitive class such as int/uint/String/etc.

You cannot clone everything with it. Serialization of DisplayObjects fails silently.