AS3: calling a property by name or by reference - actionscript-3

I am kinda new to AS and stumbled upon a "funny looking" feature in the documentation:
You can use the Object class to create associative arrays. At its core, an associative array is an instance of the Object class, and each key-value pair is represented by a property and its value. Another reason to declare an associative array using the Object data type is that you can then use an object literal to populate your associative array (but only at the time you declare it). The following example creates an associative array using an object literal, accesses items using both the dot operator and the array access operator, and then adds a new key-value pair by creating a new property:
Copy var myAssocArray:Object = {fname:"John", lname:"Public"};
trace(myAssocArray.fname); // John
trace(myAssocArray["lname"]); // Public
myAssocArray.initial = "Q";
trace(myAssocArray.initial); // Q
from here.
I understand that there are cases where this can be helpful, like this one but with a backround in mostly typesafe languages like Java and C# I am a little bit confused about which access-operator is common practice and why.
Normally I would go with the dot oporator, as it allows me and the compiler to keep track of all given properties and you are save regarding typos.
The code I am looking at right now uses both, with no recognizable pattern, which is even more confusing.
Any input on this? Is one better than the other? Why? When to use which one?

Normally I would go with the dot oporator, as it allows me and the
compiler to keep track of all given properties and you are save
regarding typos.
You are not safe against typos. When you create an Object, any property that you haven't defined/assigned to will just return undefined.
var awd:Object = {}
awd.aaa++ //NaN
awd ['aaa']++ //NaN
The compiler will not catch any attempt to reference a property that hasn't been defined.
I use the [] method almost exclusively because it does everything I would need the . method to do plus more. The biggest advantage for me is that I can access properties via variables.
var awd:Object = {}
var key:String = 'some_key';
awd [key] = 1;
awd.key = 5; //This will literally assign to the 'key' property, not what I want

Related

Actionscript: Why does this Event Listener cause massive memory consumption

I have an array of buttons and I attached event listeners like this.
arr[c].addEventListener(MouseEvent.MOUSE_UP, Proxy.go(this, click, Model.categoriesListXml.category_0[i].category_1[j].#category_id, Model.categoriesListXml.category_0[i].category_1[j].#name));
150 of these used 32MB of memory.
When I used the following memory dropped to 2MB.
var categoryId:String = Model.categoriesListXml.category_0[i].category_1[j].#category_id;
var name:String = Model.categoriesListXml.category_0[i].category_1[j].#name;
arr[c].addEventListener(MouseEvent.MOUSE_UP, Proxy.go(this, click, categoryId, name));
All I did was put the xml elements their own vars before using in the event listener.
Does anyone know why this is happening?
My guess is that the entire XML object gets included rather than just the elements I need.
I think it works as the following.
Try. Flash is very lazy when it comes to disposing anything XML-related. So lazy, in fact, that it even has System.disposeXML(...) method because otherwise an XML object might not be garbage collected even if you consciously remove every single reference to it.
Catch. It is important to understand, that most XML operations result in XML or XMLList object, say
// XML source.
var X:XML = <root a="1" />;
// You might think it returns String, but no, it is just autocast to String.
var a:String = X.#a;
// If you don't specify data type, this returns XMLList object of length 1,
// with an XML member of type "attribute", name "a" (or maybe "#a"), and so on.
var A:* = X.#a;
So, without explicitly casting your attribute to String you pass 2 XMLList objects as the function arguments instead (or so it seems).
Finally. Just looking at Proxy.go(...) tells us that it creates a delegate (which is a type of closure), an unnamed unbound function with the stored arguments list. It should look similar to this:
public function go(target:Object, method:Function, ...rest:Array):Function
{
return function():void
{
method.apply(target, rest);
}
}
This works due to ECMA standard (probably works with JavaScript as well) that allows closures to access all their parent method data: local variables and method arguments.
So, you have it. Some unnamed function keeps (pretty much forever) somewhere in the player's memory a list of untyped arguments, which contains your XMLList objects (which are persistent and not easily disposed of). Then you create 150 such monstrosities this way. It's only natural that memory leaks in fountains and niagaras.

AS3: how to pass by "object"

I was actually looking for a way to pass by reference in AS3 but then it seemed that adobe and lots of people's understanding of pass by reference is different from what I have been taught at the university. I was taught java was pass by value and C++ allowed pass by reference.
I'm not trying to argue what pass by value and reference are. I just want to explain why I'm using pass by object in the question...
Back to the question, I would like to do something like:
public function swapCard(cardA:Cards, cardB:Cards) {
var temp:Cards = cardA;
cardA = cardB;
cardB = temp;
}
...
swapCard(c1, c2);
EDIT: adding two examples on how I'm using the swapCard function
1) in the process of swaping a card between player1 and player2's hand
swapCard(player1.hand[s], player2.hand[t]);
2) in the process of swaping a card between player1's hand and deck
swapCard(player1.hand[s], player1.deck[rand]);
In C++, we only need to add a symbol before the parameters to make it work (and we call THIS pass by reference). But in AS3, cardA and cardB are just pointers to the formal parameters. Here in the function, changing the pointers does not do anything to the formal parameters :(
I have been searching for hours but I couldn't find a way to without knowing all the properties of the Cards.
If I have to change the properties of the cards one by one then maybe I should change swapCard to a static function in class Cards? (because I don't want to expose everything to another class) I'm not sure if this is a good practice either. This is like adding a swap_cars function into class Cars. If I let this happen, what will be next? Wash car, lend car, rent car... I really want to keep the Cards class clean and holds only the details of the card. Is there a possible way to do this properly in AS3?
The kind of swap function that you're trying to implement is not possible in AS3. The input parameters are references to the input objects but the references themselves are passed by value. This means that inside the function you can change the cardA and cardB but those changes will not be visible outside the function.
Edit: I added this portion after you edited your question with sample usage.
It seems like you're trying to swap two objects in 2 different arrays at given array positions in each - you can create a function for this in AS3 but not the way you attempted.
One possible implementation is to pass the arrays themselves and the positions that you're trying to exchange; something like this:
// Assumes arrays and indices are correct.
public function SwapCards(playerHand:Array, playerCardIndex:int,
playerDeck:Array, playerDeckIndex:int):void
{
var tempCard:Card = playerHand[playerHandIndex];
playerHand[playerHandIndex] = playerDeck[playerDeckIndex];
playerDeck[playerDeckIndex] = tempCard;
}
Note that you still exchange references and the arrays themselves are still passed by reference (and the array references are passed by value - you could, if you wanted, change the arrays to new arrays inside this function but you wouldn't see new arrays outside). However, because the array parameters refer to the same arrays inside and outside the function, you can make changes to the contents of the array (or other array properties) and those changes will be visible outside.
This solution is faster than cloning the card because that involves allocating memory for a new Card instance (which is expensive) and that temporary instance will also have to be freed by the garbage collector (which is also expensive).
You mentioned in a comment that you pass cards down to lower levels of code - if you don't have a back reference to the arrays (and the positions of the cards), you will not be able to easily swap cards - in AS3, all input parameters are copies (either the copy of the value for primitive types or the copy of the reference for complex objects - changes to the input parameters in a function will not be visible outside).
EDIT: renaming the function from clone to copyFrom as pointed out by aaron. Seems like clone is supposed to be used as objA = objB.clone()
At this point, I'm adding a copyFrom() function in the Cards class such that
var temp:Cards = new Cards(...);
var a:Cards = new Cards(...);
...
temp.copyFrom(a);
...
temp will be copying everything from a.
public function swapCard(cardA:Cards, cardB:Cards) {
var temp:Cards = new Cards();
temp.copyFrom(cardA);
cardA.copyFrom(cardB);
cardB.copyFrom(temp);
}
I will wait for a week or so to see if there are any other options
You have some good answers already, but based on the comments back-and-forth with me, here's my suggestion (I use "left" and "right" naming because it helps me visualize, but it doesn't matter):
function swapCard(leftCards:Array, leftCard:Card, rightCards:Array, rightCard:Card):void {
var leftIndex:int = leftCards.indexOf(leftCard);
var rightIndex:int = rightCards.indexOf(rightCard);
leftCards[leftIndex] = rightCard;
rightCards[rightIndex] = leftCard;
}
Now you can swap the cards in the two examples you posted like this:
swapCard(player1.hand, player1.hand[s], player2.hand, player2.hand[t]);
swapCard(player1.hand, player1.hand[s], player1.deck, player1.deck[rand]);
However, note that while this swaps the cards in the arrays, it does not swap direct references to the cards in those arrays. In other words:
var a:Card = player1.hand[0];
var b:Card = player2.hand[0];
swapCard(player1.hand, a, player2.hand, b);
// does not change the references a and b, they still refer to the same card
a == player2.hand[0];
a != player1.hand[0];
b == player1.hand[0];
b != player2.hand[0];
Typically, this sort of thing is handled by dispatching a "changed" event so that any code that cares about the state of a player's hand array will know to re-evaluate the state of the hand.
There's a deep misunderstanding going on here. The question is about object reference but the PO is not trying to swap any Object reference at all.
The problem comes from the fact that the PO does not understand the difference between variable and objects. He's trying to swap variable/object reference which is not dynamically possible of course. He wants with a function to make the variable holding a reference to Object A, swap its object reference with another variable. Since Objects can be passed around but not variables (since they are just holders (not pointers)) the task is not possible without a direct use of the given variable.
To resume:
variables are not Objects!
variables hold a reference to an object.
variables cannot be passed in function or referenced in functions because THEY ARE NOT OBJECTS.

Compatability when passing object to class

Ok, so this might be me being pendantic but I need to know the best way to do something:
(This is psudocode, not actual code. Actual code is huge)
I basically have in my package a class that goes like this:
internal class charsys extends DisplayObject {
Bunch of Variables
a few functions
}
I another class which I intend to add to the timeline I want to create a function like this:
public class charlist {
var list:Array = new Array();
var clock:Timer = new Timer(6000);
var temp:charsys;
function addObj(MC:DisplayObject, otherprops:int) {
temp=MC;
temp.props = otherprops;
list.push(temp)
}
function moveabout(e: event) {
stuff to move the items in list
}
function charlist() {
stuff to initialize the timers and handle them.
}
}
So the question is, is my method of populating this array a valid method of doing it, is there an easier way, can they inherit like this and do I even need to pass the objects like I am?
(Still writing the package, don't know if it works at all)
Yes, you can pass an object into a function, but you should be careful of what you are planning to do with that object inside that function. Say, if you are planning to pass only charsys objects, you write the function header as such:
function addObj(MC:charsys, otherprops:int) {
Note, the type is directly put into the function header. This way Flash compiler will be able to do many things.
First, it will query the function body for whether it refers to valid properties of a passed instance. Say, your charsys object does not have a props property, but has a prop property, this typing error will be immediately caught and reported. Also if that props is, for example, an int, and you are trying to assign a String value to it, you will again be notified.
Second, wherever you use that function, Flash compiler will statically check if an instance of correct type charsys is passed into the function, so if there is no charsys or its subclass, a compilation error is thrown.
And third, this helps YOU to learn how to provide correct types for functions, and not rely on dynamic classes like MovieClip, which can have a property of nearly any name assigned to anything, and this property's existence is not checked at compile time, possibly introducing nasty bugs with NaNs appearing from nowhere, or some elements not being displayed, etc.
About common usage of such methods - they can indeed be used to create/manage a group of similar objects of one class, to the extent of altering every possible property of them based on their corresponding values. While default values for properties are occasionally needed, these functions can be used to slightly (or not so slightly) alter them based on extra information. For example, I have a function that generates a ready-to-place TextField object, complete with formatting and altered default settings (multiline=true etc), which is then aligned and placed as I need it to be. You cannot alter default values in the TextField class, so you can use such a function to tailor a new text field object to your needs.
Hope this helps.
This would work, I think I would assign values to the properties of the charsys object before passing it into the add method though, rather than passing the properties and having a different class do the property assignment. If you have some common properties they could either have defaults in charsys class definition or you could set literals in the addObj method.

How can I iterate Dynamic object in Haxe

I have Object parsed from JSON (haxe.Json.parse()) and I need to iterate over it.
I already tried to cast this object to Array<Dynamic>:
var data:String='{"data":{"0":0,"1":1},"method":"test"}';
var res:{method:String,data:Array<Dynamic>} = haxe.Json.parse(data);
for (n in res.data)
trace('aa')
There is no Can't iterate dynamic exception, just not working (iterating).
I completley don't understand why in Haxe iterating procedure is so difficult.
For the sake of posting a complete answer, and in case other people are wondering
In your first example, you've told the compiler that "res" contains two properties - one called "method" (which is a String) and one called "data" (which is Array). Now the JSON you're using doesn't actually have an Array<Dynamic>, it just has a dynamic object. An Array would look like: "data":[0,1].
So, assuming you meant for the JSON to have data as a Dynamic object, here is how you loop over it, using Reflect (as you mentioned in the comments):
var data:String='{"data":{"0":0,"1":1},"method":"test"}';
var res = haxe.Json.parse(data);
for (n in Reflect.fields(res.data))
trace(Reflect.field(res.data, n));
Note here we don't have to specify the type of "res", since we're using Reflection just leaving it as Dynamic will be fine.
Now, if your JSON actually contains an Array, the code might look like this:
var data:String='{"data":[0,1],"method":"test"}';
var res:{method:String,data:Array<Int>} = haxe.Json.parse(data);
for (n in res.data)
trace(n);
Here you use explicit typing to tell the compiler that res.data is an Array (and this time it actually is), and it can loop over it normally.
The reason you didn't get an error at compile-time is because the compiler thought there was genuinely going to be an array there, as you told it there was. At runtime, whether or not it throws an exception probably depends on the target... but you probably want to stay out of that anyway :)
Demo of both styles of code: http://try.haxe.org/#772A2

How can I create Vectors dynamically in AS3?

I want to create a class that will mainly house a Vector. The class will have some methods that deal with items in the Vector.
The issue I am having at the moment is that I can't work out how to dynamically create an instance of Vector. So far I've tried this and similar with no luck:
public class List
{
private var _content:Vector;
public function List(type:Class)
{
_content = new Vector.<type>();
}
}
Here is how I dynamically construct a vector of BitmapData (which is required by the MouseCursorData class):
var vectorClassOfBitmapData:Class = Class(getDefinitionByName("__AS3__.vec::Vector.<flash.display::BitmapData>"));
var bitmapDataVector:* = new vectorClassOfBitmapData(1,true);
The above is the same as the compile-time:
var bitmapDataVector:* = new Vector.<BitmapData>(1, true);
In this way, you can compose the class definition string at runtime and use getDefinitionByName to dynamically construct vectors of different data types.
Not exactly what you were after, but it might help others.
This post by Paul Robertson (previously Senior ActionScript Developer/Writer at Adobe) provides a little more information on how Vectors are declared:
The Vector class allows (requires) you to specify the type it will
contain at compile time — both for variable declarations and when
creating instances.
Because the type parameter is a literal, it must be provided at compile time. In fact, every reference to a Vector is checked at compile time, with the exception of .shift() and .unshift, which are checked at run time.
Adobe's article on indexed arrays provides some more interesting information on that. In fact, it mentions that strict compile time type safety is one of the key features of Vectors.
In short: It is not possible to use a variable to set a Vector's type, because the type parameter is a literal and a compile time requirement.
Hope that helps!
Additional References:
http://www.adobe.com/devnet/flash/quickstart/programming_vectors_as3.html
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/Vector.html
http://www.mikechambers.com/blog/2008/08/19/using-vectors-in-actionscript-3-and-flash-player-10/
How do generics (Vector) work inside the AVM?
Another option that might work for you is to use an interface; Vectors do not have to be concrete types. So if you can abstract out some common contract that your objects can abide by, then use that instead. For example, say you wanted a list of renderable objects, you could say:
public interface IRenderable {
function renderTo(obj:DisplayObject):void;
}
var _content:Vector.<IRenderable> = new Vector.<IRenderable>();
Then you can shove as many different concrete types into the Vector, as long as they implement the IRenderable interface. So while generics in ActionScript 3 are really just syntactic compiler sugar, like Andrew Odri said, you might be able to get around that depending on what you are specifically trying to do.
Sounds like you just need an Array! The performance is only improved with a Vector<> because the type is sorted out at compile time. If you want a "dynamic" type, then you should use an Array.
The original question is a couple years old, but I felt like sharing this because it might help others.
It's inspired upon Matthew Peterson's answer but it assumes a little less about the internal class names (it only assumes the .<> syntax).
function CreateVectorOf(subtype:Class)
{
var vecname:String = getQualifiedClassName(Vector);
var subname:String = getQualifiedClassName(subtype);
var vecclass:Class = getDefinitionByName(vecname + ".<" + subname + ">") as Class;
return new vecclass();
}