How to pass by reference in ActionScript? - actionscript-3

I'm trying to modify an Object in another class. So I have something like this:
MainClass.as:
..
var myObject:Object = new Object();
Class2_Instance.get_JSON(myObject);
trace(myObject.id); // output: undefined. whereas it should be 42. see below.
..
Class2.as
public function get_JSON(url:String , the_object:Object)
{
var request:URLRequest = new URLRequest(url);
var variables:URLLoader = new URLLoader();
variables.dataFormat = URLLoaderDataFormat.TEXT;
variables.addEventListener(Event.COMPLETE, Complete_Handler_JSON(the_object));
try
{
variables.load(request);
}
catch (error:Error)
{
trace("Unable to load URL: " + error);
}
}
function Complete_Handler_JSON(the_object:Object):Function
{
return function(event:Event):void
{
var loader:URLLoader = URLLoader(event.target);
the_object = JSON.parse(loader.data);
trace(the_object.id); //returns 42.
};
}
So the JSON operation performs correctly within Class2, and it assigns the .parse() value to the_object, but I think there is something I don't understand with AS's pass-by-reference logic. Since I was expecting myObject in MainClass.as would change, too.
What should I do to modify the value of the function argument (myObject) directly ?
Thanks !

The issue is in this line in your event handler:
the_object = JSON.parse(loader.data);
The minute you do this, you're no longer dealing with the object which you passed in to the method. You're assigning a new value to the local variable named the_object. It's important to understand that objects are not passed by reference - object references are passed by value. So the_object in your method is a copy of the reference. When you assign a new value, that copy is overwritten with a different reference.
The solution is to pass the reference by reference - this is related to the concept of double pointers in languages such as C++, also known as double indirection. I'm not certain that this is even possible in ActionScript.
A better solution, just return the deserialized object as a return value.

Objects are passed by reference.
But as i see in your code you are overwriting the_object by another one on the line : the_object=JSON.parse(loader.data).
You can create a new variable and then copy the values in the_object:
var json:Object = JSON.parse(loader.data);
for (var k:String in json) {
the_object[k]=json[k];
}

Related

Convert an Object's Name to a String

var variable:Object=new Object();
How would you convert "variable" as an object to "variable" as a string? I thought this would work:
var variable:Object=new Object();
var variable_string=String(variable);
You cannot get the name of a variable that holds an instance via said instance.
You could store the instance in an Object against a given key, which could be found using a for...in loop:
var myObject:Object = {};
var objects:Object = { variable: myObject };
for(var i:String in objects)
{
if(objects[i] === myObject)
{
trace(i); // variable
break;
}
}
All class level member names are stored in the string constant pool in your compiled SWF. Thus, it is in fact technically possible to get the name of variable by loading the bytes of the SWF and parsing them (or by using someone else's API to do it). However, this is probably more trouble than it's worth for whatever it is you're doing.

Actionscript - how to pass object by value?

I want to pass an object to a function by value so that I can make modifications to that object. I don't want the original object to be updated. However, all the function parameters are passed by reference.
I've tried to copy an object ( var new_object:Object = original_object; ) This just creates a pointer to original_object.
Is there a way I can pass parameter by value?
update One workaround I see is to make deep copy of an object by using ByteArray as described here. Not sure how efficient it is. Maybe there is a better solution out there.
You will have to make a copy of the object before passing it to the function :
public function copy(value:Object):Object
{
var buffer:ByteArray = new ByteArray();
buffer.writeObject(value);
buffer.position = 0;
var result:Object = buffer.readObject();
return result;
}
public function testFunction(obj:Object):void
{
//do something with obj
}
public function test():void
{
var obj:Object = {};
testFunction(copy(obj));
}

as3 operator= /(object assign )

I know that AS3 does not have pointer or reference. Every object is pass by reference already. (I supposed?)
What should I do if I want to do with pointer?
e.g. all object point to one target, I only need to change target's value, then all other object will access different value.
You can effectively get the same behavior by using a helper object to simulate a pointer -- in other words using it to carry the target reference. For instance:
public class PseudoPointer
{
private var obj:Object;
private var prop:String;
public function PseudoPointer(obj:Object, prop:String)
{
// Point to property with name 'prop' on object 'obj'.
this.obj = obj;
this.prop = prop;
}
public function get value():* {
return obj[prop];
}
public function set value(value:*):void {
obj[prop] = value;
}
}
Then you could do this -- assume there's a magicNumber property on an object named target:
var numberPtr = new PseudoPointer(target, "magicNumber");
myDynamicObjectA.numberPtr = numberPtr;
myDynamicObjectB.numberPtr = numberPtr;
myDynamicObjectC.numberPtr = numberPtr;
Now any object that has a reference to the pseudo-pointer can read and write the target property:
numberPtr.value = 42;
You could create a function and in which you give it the value and then subsequently assign it to all of those other variables.
Something like below:
var objectA:Number;
var objectB:Number;
...
function myFunction(newValue:Number):void
{
objectA = newValue;
objectB = newValue;
...
}
You could try setting a variable reference to a function. Then if you update that reference, it would return a different function.
var myFunc:Function;
function funcOne():int {
return 1;
}
function funcTwo():int {
return 2;
}
function getFunc():Function {
return myFunc;
}
myFunc = funcOne;
var myObjOne:Object = new Object();
myObjOne.objFunc = getFunc;
var myObjTwo:Object = new Object();
myObjTwo.objFunc = getFunc;
trace(myObjOne.objFunc.call().call()); // 1
trace(myObjTwo.objFunc.call().call()); // 1
myFunc = funcTwo;
trace(myObjOne.objFunc.call().call()); // 2
trace(myObjTwo.objFunc.call().call()); // 2
This allows any object to point at a single function and have what that returns be updateable for all of them simultaneously. It's not the prettiest code and it's not as type-safe as if ActionScript had delegates to enforce the signatures of what's set for myFunc, but it is still a pretty flexible model if you get creative with it.
Have those pointers point to something that contains the object or has the object as a property on it.
So
var myArr:Array = [new YourObject()];
var client1:ArrayClient = new ArrayClient();
client1.array = myArr;
var client2:ArrayClient = new ArrayClient();
client2.array = myArr;
myArr[0] = new YourObject();
//client1.array[0]==client2.array[0]==the new object
However, this seems to be a great way to get yourself into a lot of trouble really quickly. What's the use case?

Actionscript 3 Flex 4.5 Serialization/Deserialization issue?

I'm having a particular issue deserializing an object in a Flex 4.5 Mobile project. I've connected to a Webservice fine and populate a ListBox fine. I can select the item and get the details just fine as well but after serializing the object and trying to deserialize it; the Object definition is getting lost somewhere.
I have a variable for when the user selected the request in a List
private var selectedReq:ServiceRequest;
//Here we instantiate the local variable when user select id in ListBox
selectedReq = event.currentTarget.selectedItem as ServiceRequest;
Each Service Request the user chooses to save will call this method.
private function writeServiceRequest():void {
var filename:String = buildFileName();
var file:File = File.applicationStorageDirectory.resolvePath(filename);
if (file.exists)
file.deleteFile();
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.WRITE);
//selectedReq is the private var of the users selected item
fileStream.writeObject(selectedReq);
fileStream.close();
}
When the users want to view the request this method is called.
private function readServiceRequest():ServiceRequest {
var filename:String = buildFileName();
var file:File = File.applicationStorageDirectory.resolvePath(filename);
if (!file.exists) {
return null;
}
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.READ);
var objReq:ServiceRequest = fileStream.readObject() as ServiceRequest;
fileStream.close();
return objReq;
}
The Class Object is similar to.
public var id:uint;
public var requisitioner:String;
public var requestItems:ArrayCollection //Webservice it's actually List<requestItems>
public var requestProcesses:ArrayCollection // WSDL it's actually List<>
When I try to read/deserialize like
//This line is null but the file exist and the object was written
var objReq:ServiceRequest = readServiceRequest() as ServiceRequest;
if(objReq) {
selectedReq = objReq;
}
If I do not cast the readServiceRequest() as ServiceRequest and simply return an Object; I can iterate through the Object and get the correct values returned from the serialized object.
I can only assume the Classes that Flex created from the Webservice may be causing this? If the values are getting written but not the object type then something has to be lost in the serialization - correct?
Sorry for all the details but I'm a little lost at this time.....any help would be appreciated.
RL
var objReq:ServiceRequest = readServiceRequest() as ServiceRequest;
The above line will continue to return null.
I bet that if you modify it in the following way:
var objReq:ServiceRequest = ServiceRequest(readServiceRequest());
You'll get a run-time exception with a message similar to Can't cast ObjectProxy to ServiceRequest
If that's the case, then the reason you get this is because the AMF serializer doesn't preserve the type information when serializing the ServiceRequest-instance.
In order to fix this you need to call flash.net.registerClassAlias() before the serialization/deserialization.
flash.net.registerClassAlias("fully.qualified.name.ServiceRequest", ServiceRequest);
Try if this work.
Check if event.result is ByteArray.
Read/store the event.result as ByteArray.
var byteArray:ByteArray = event.result as ByteArray;
Get/Deserialize Object using readObject() function.
var object:Object = byteArray.readObject();
Cast to targetted object type. ServiceRequest in your case.
var tartgettedObject:ServiceRequest = object as ServiceRequest;
or
var tartgettedObject:ServiceRequest = ServiceRequest(object);
My bad for not signing in before posting the question.
I did use registerClassAlias which did not help. I did find on the Adobe forum other simular issues and the constant feedback was the problem is most likely caused by the Object Class loosing it's attributes during the file write reason being the Class Objects are written when the Datasource is created from the Webservice. Since my original question we decided to use SQLite which is working perfectly fine - thanks for all you help.
RL

interaction with keyboard/textfields help please

Normally for a string to object is converted as follows.
var obj:object=getChildByName("string");
And we can give properties to it like obj.x=100;But in the case of a series of stings
[objet Stage].[object MainTimeline].[object TextField]
it wil not works.Actually i need to give properties to a target path which is a string
what i do??
Here is the code to get path to a movieclip:
addEventListener(MouseEvent.CLICK, targetMC);
function targetMC(MouseEvent:Event):void
{
var curinstance = MouseEvent.target.valueOf();
var targ:Object = curinstance.parent;
var path = curinstance;
do
{
if (targ == "[object Stage]")
{
path = targ + "." + path;
}
else
{
path = targ + "." + path;
}
targ = targ.parent;
} while (targ);
trace(path);
}
i would like to give properties to path.
A number of things are awkward about your code:
Don't compare the string value of objects to find out about class type. Use the is keyword:
if (obj.parent is Stage) doSomething();
Don't use class names as parameter names: MouseEvent is a type!
function targetMC ( ev:MouseEvent ) // ...more code
It is useful to name handler methods according to the event upon which they are invoked, for example:
function onMouseClick (ev:MouseEvent)
or
function mouseClickHandler (ev:MouseEvent)
If you can avoid it, don't cast to Object to access members, but try to use subclass types - it allows the compiler to more effectively check your code for errors. Since all objects in the display list are instances of DisplayObject, you could use this:
var obj:DisplayObject = ev.target as DisplayObject;
If you want to output a path to your object, use instance names instead of types - you might have more than one TextField!
private function getObjectPath (obj:DisplayObject) : String {
var path:String = obj.name;
if (obj.parent != null && !(obj.parent is Stage)) {
path = getObjectPath (obj.parent) + "." + path;
}
return path;
}
Now for your answer: Use the KeyboardEvent.
textField.addEventListener (KeyboardEvent.KEY_UP, onKeyUp);
and
private function onKeyUp (ev:KeyboardEvent) : void {
var tf:TextField = ev.target as TextField;
var text:String = tf.text;
tf.text = text + String.fromCharCode(charCode);
}
Note that this will only work as long as the TextField has focus, that is the user has to click it first.
If you need to pass a key charCode to a TextField, the latter should listen to a KeyboardEvent and retrieve the info from the event's charCode property
http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/
Your perspective of AS3 is "different"... For instance getChildByName doesn't convert a String to an Object, it basically does what the method name states , it retrieves a parent's child using its name as a reference.
It looks like you're adapting whatever language you're coming from to AS3. I doubt this will work...