views:

536

answers:

4

I thought I had references in AS3 figured out, but the following behavior puzzles me:

// declarations for named individual reference later on
var myAmbientSound:Sound;
var myAmbientSoundLocation:String = "http://ambient_sound_location";

var myPeriodicSound:Sound;
var myPeriodicSoundLocation:String = "http://periodic_sound_location";

var myOccasionalSound:Sound;
var myOccasionalSoundLocation:String = "http://occasional_sound_location";

// for iterating through initialization routines
var mySoundArray:Array = [myAmbientSound, myPeriodicSound, myOccasionalSound];
var mySoundLocation:Array = [myAmbientSoundLocation, myPeriodicSoundLocation, myOccasionalSoundLocation];

// iterate through the array and initialize
for(var i:int = 0; i < mySoundArray.length; i++) {
    mySoundArray[i] = new Sound();
    mySoundArray[i].load(new URLRequest(mySoundLocation[i]));
}

At this point, I'd think that mySoundArray[0] would reference the same object as myAmbientSound; however, accessing myAmbientSound throws a null pointer exception, while mySoundArray[0] works as expected and references a Sound object. What am I misunderstanding here?

A: 

Defining a variable isn't creating a pointer.

// This line is only creating a QName, but no pointer.
// Value of "myAmbientSound" is "null".
var myAmbientSound : Sound;

// Here, you put the reference pointed by myAmbientSound into the array.
// It is null, so mySoundArray[0] will also be null.
var mySoundArray : Array = [myAmbientSound];

// Here, you assign a new pointer to the index 0 of mySoundArray.
// It is simply overwriting the "null" value, but there is no
// connection to myAmbientSound.
mySoundArray[0] = new Sound();

// myAmbientSound is still null, as it is still pointing to nothing.
trace(myAmbientSound);  // null
// But index 0 of mySoundArray holds a pointer to the sound you instanciated.
trace(mySoundArray[0]); // [Object Sound]

To achieve what you are looking for, you must do something like that :

// Creating the variable and pointing it to an object instance.
var myAmbientSound : Sound = new Sound();

// Here, the pointer isn't null, so the reference can be added to the array.
var mySoundArray : Array = [myAmbientSound];

// And now, both accessors points to the same object instance.
trace(myAmbientSound);  // [Object Sound]
trace(mySoundArray[0]); // [Object Sound]
Tyn
+4  A: 

It is more like java reference variables than C pointers.

var myAmbientSound:Sound;
var myPeriodicSound:Sound;
var myOccasionalSound:Sound;
//these variables are not initialized and hence contain null values

Now you create an array containing the current values (null) of these variables

var mySoundArray:Array = [myAmbientSound, myPeriodicSound, myOccasionalSound];

The array now contains three nulls [null, null, null], and not three pointers to Sound objects as you expect it to contain.

Now when you call mySoundArray[0] = new Sound(); a new Sound object is created and its address is assigned to the first location of the array - it doesn't modify the myAmbientSound variable.

Amarghosh
`null` isn't exactly the same as `undefined`. The difference is trivial, but can help understanding this behaviour. An uninitialized variable is pointing to the `undefined` value.
Tyn
@Tyn I beg to disagree. In case of typed variables, "For data types other than `Boolean`, `Number`, `int`, and `uint`, the default value of any uninitialized variable is null . This applies to all the classes defined by ActionScript 3.0, as well as any custom classes that you create." http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f9d.html
Amarghosh
@justin Even in case of C pointers/references, I guess those variables need to be initialized for the code to work as expected, isn't it?
Amarghosh
@Amarghosh Exact, my mistake. AS2 isn't totally forgotten, I guess. :)
Tyn
The last sentence of this answer is the key. When you do `someArray[0] = new SomeObject();`, you *replace* `someArray[0]` with the new `SomeObject`.
Selene
A: 

In general, references in ActionScript, Java, and other languages are not treated like references in C/C++. If references worked as in C/C++, the code you wrote would be correct and work as you expected. In general, you should try to think of references as auto-dereferenced pointers. Your code is a great example of the difference between C/C++ references and modern references.

Here's an example of the consequences of this behavior as it applies to the garbage collector:

var someRef:Sound = new Sound();
someRef = null;

Then the Sound object I created in the first line still exists, and may continue to exist. This is important to remember since if you register event listeners in AS3, you create an extra reference to them, preventing them from being destroyed by the GC. That's why you should almost always use the full call to AddEventListener, forcing it to use weak references (which aren't counted for the GC).

Additionally, keep in mind that all function calls are ByVal in AS3. So this code wouldn't work:

function initSound(a:Sound, b:String):void {
    a = new Sound();
    a.load(new URLRequest(b);
}

var myAmbientSound:Sound;
var myAmbientSoundLocation:String = "http://ambient_sound_location";
initSound(myAmbientSound, myAmbientSoundLocation);

The good news is that most languages with built-in garbage collection have the same behavior for references, so once you get the hang of it, you'll be able to pick up those languages even quicker.

SEK
+1  A: 

Since others have taken their time and have come up with some very good explanations, I will try to address your questions (within your question) explicitly:

At this point, I'd think that mySoundArray[0] would reference the same object as myAmbientSound

But myAmbiendSound does not reference anything? Look, you wrote this yourself:

var myAmbientSound:Sound;

The above just creates a local variable of type Sound, a reference. All local variables are references, but not all references are local variables. Object properties are also references, for example. Anyway, the expression above does not create an object of class Sound. You just created a locally scoped reference with a special value of null, that's it. It may reference a Sound object though. There is, however, a difference between creating a variable and creating an object and/or referencing it. The above creates a variable. Operator new creates an object and returns a reference to it. The assignment operator = makes the reference on the left side reference the object/value referenced by the reference on the right side. Yes, that sentence does not read good at all, but it describes the logic exactly. During its lifetime, a reference can equally well reference many different objects at different points in time. One way to have the local variable above reference an object would be:

myAmbientSound = new Sound();

In your case however, the variable holds a special null value instead, since you did not assign it any object reference. And it does so during the entire runtime of your snippet.

Before I explain what you end up putting into your array with the line:

 var mySoundArray:Array = [myAmbientSound ...

, you have got to understand that array elements, much like object properties, are references too - e.g. mySoundArray[0] may holds a reference to an object, and mySoundArray[1] and so on.

Assigning using the above syntax - [ value1, value2, value3 ... ] - creates a new array object, and establishes its element values as references to whatever "value1", "value2", "value3" reference each, respectively.

Now, since we have established that the local variable called "myAmbiendSound" has a null value, your first array "element" hence also ends up having a null value. Initially. Because in the loop later on you have the first element (or, literally, element at index number referenced by variable i) reference a new Sound object. "At that point", since myAmbiendSound is still (and for the entire scope of your snippet) null, of course comparing the two for equality will fail - they do not reference the same object - the former is null and the latter is assigned a reference to the Sound object.

Also, a minor correction: accessing an object does not throw an exception. Accessing properties of an non-object - i.e. using dot notation syntax on a reference that is null, does result in a thrown exception, however.

I hope this clears matters up. I consider it an addition to all said earlier here.

amn