简体   繁体   中英

Actionscript 3 - Reference vs. Value

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?

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.

Since others have taken their time to come up with some very good explanations, I will instead try to address the questions you ask implicitly:

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

But myAmbiendSound does not reference anything. From your code:

var myAmbientSound:Sound;

The above just creates a local variable of type Sound, a potential reference. A "finger" that can point to something, in laymans terms. All local variables are references. And by the way, not all references are local variables, object properties are references too, as are Array and Vector elements. Anyway, the expression above does not create an object of Sound class. It merely declares a local variable that may reference an object of the Sound class. Upon declaration it has a special value of undefined .

There is a difference between declaring a variable like above and assigning it a value. Operator new creates an object and returns a reference to it. The assignment operator, = , results in whatever is on the left side reference whatever is on the right side, put roughly.

One way to have the local variable above reference an object would be:

myAmbientSound = new Sound();

In your case however, like I said, the myAmbientSound variable has a special undefined value instead, since you merely declared it, and it hasn't yet been assigned a value. Also, as we can see from the code, it does not reference anything during the entire runtime of your code snippet.

Now, before I explain what you end up putting into your array with the line:

 var mySoundArray:Array = [ myAmbientSound, ...

, you have to remember that array elements, much like object properties, are references too - eg mySoundArray[0] may reference an object, as may mySoundArray[1] and so on.

The line of code above declares a new local variable and makes it reference a new array object. Declaration and definition in one single statement.

Now, since we have established that your local variable myAmbiendSound holds the special undefined value, your first array element ends up referencing that same value - undefined , initially . In the loop later on you have the first element (or, if we are to be prudish, element at the number referenced by variable i ) reference a new Sound object. At that point (and for the entire scope of your snippet, like we observed), since myAmbiendSound is undefined , comparing the two for equality will fail - they do not reference the same object, nor are the objects they reference considered "equal" (how = works in AS3 is another topic) - the former is undefined and the latter points to a Sound object.

Also, a minor correction: "accessing" an object does not result in an run-time exception. Attempting to access a property (using dot notation syntax) of a reference that does not point to an object - does result in a run-time exception however. It's as if you'd write trace(null.foo) - null is not an object, and certainly has no property named foo . Same goes for undefined .

By the way, null == undefined is true , but null === undefined is false . Just saying. On a seldom occation (depending, of course), you will have to honor this detail.

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

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.

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]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM