Arraycollection is being passed into function by value instead of by reference in flex 3
Asked Answered
B

4

9

I want to set arrayCollection #2 = to arrayCollection #1 via a function in flex 3. I pass both array collections to a function and set arrayCollection #2 = arrayCollection #1. However, it seems to not be passing arrayCollection #2 by reference because after the function call, arrayCollection #2 has not been changed. My understanding is that it should be passed by reference and work, am I doing something wrong? Below is the code:

var AC1:ArrayCollection = new ArrayCollection;
var AC1.addItem(someObject);

var AC2:ArrayCollection = new ArrayCollection;

setAC2(AC1,AC2);
// AC2 is not set to AC1 after the function


private function setAC2(_ac1:ArrayCollection, _ac2:ArrayCollection):void
{
    _ac2 = _ac1;
}
Bananas answered 6/5, 2011 at 22:15 Comment(1)
+1 For getting the terms correct, by accident or not. See Evaluation Strategy. Although, "pass by value [of object reference]" is better called "pass by object" or "pass by object-sharing" to avoid this confusion. I just know someone is going to say "But they are passed by reference!" (and this may have even led to the current confusion if the term was misused in some documentation or tutorial ;-)Lovely
L
5

Please see Evaluation Strategy.

AS uses "pass by object" / "pass by object-sharing". That is, the "object" is passed (not a copy, clone or duplicate) and any modifications to the object are shared.

However, the assignment _ac2 = _ac1 only changes the value of the [function's local] parameter variable and will have no affect on any variables during the function invocation. The only thing passed in are the values ("objects") which result from the evaluation of the variables (or any arbitrary expression) used in the function invocation.

This is because, as stated above, the strategy used is "pass by object" and not (as the documentation states "pass by reference", which really means, "pass by value [of the reference]" or just ... "pass by object"). That is, the term "pass by reference" is actually misused and hence, confusing. (It is misused in a number of languages and documentation. It is an uphill battle trying to getting to a common meaning.)

If it were really "pass by reference" then assigning a new value to _ac2 would propagate out. (Before posing a comment saying how AS is "pass by reference", please see the link at top and consider that "pass by reference" covers the case of C#'s out/ref, VB's ByRef, TSQL's output and C++'s (reference) & -- these notions are not in AS, Javascript, or Java). However, as correctly noted in the original post (and additional self-reply), it is not the case -- conclusion: AS does not support "pass by reference"; furthermore, the documentation (confusingly) uses the term "pass by reference" to mean "pass by object" / "pass by object-sharing".

There are several ways that the change can be propagate out, ordered by order of (my) preference:

  1. Return the new applicable value: AC2 = doSomeTransformation(AC1). This generally the cleanest. Avoid side-effects and surprising code. Multiple values can be returned if wrapped in an object (or array) as appropriate.

  2. Use a closure: doSomeTranformation(AC1, function (newValue) { AC2 = newValue }) where doSomeTransformation might look like: function doSomeTransformation(_ac1, finished) { ...; finished(_ac1) }. I generally only use this when the callback "runs in context" of the function itself or when writing code in a CPS-style.

  3. Mutate an object (AS is "pass by object", after all). This is very icky, but it will work. var blah = {AC2: null}; doSomeTransformation(ac1, blah); ...; laterOn(blah.AC2) where doSomeTransformation might look look like function doSomeTransformation(_ac1, b) { ...; b.AC2 = _ac1; }. Not recommended in general.

Happy coding.


Applicable excerpts, from Evaluation Strategy:

"call by reference": (my main argument for "call by reference" being used incorrectly is that it already has a well-defined meaning; the overloaded term adopted by some languages such as AS and Python just adds confusion)

In call-by-reference evaluation (also referred to as pass-by-reference), a function receives an implicit reference to a variable used as argument, rather than a copy of its value. This typically means that the function can modify the variable used as argument- something that will be seen by its caller.

"call by object" / "call by object-sharing": (but pay heed to where it acknowledges the inconsistency/localization of these terms; the term "call by reference" is often misused to imply these semantics and "call by value [of the reference]" is used in some contexts to also mean the same thing)

The semantics of call-by-sharing differ from call-by-reference in that assignments to function arguments within the function aren't visible to the caller (unlike by-reference semantics), so e.g. if a variable was passed, it is not possible to simulate an assignment on that variable in the caller's scope. However since the function has access to the same object as the caller (no copy is made), mutations to those objects, if the objects are mutable, within the function are visible to the caller, which may appear to differ from call-by-value semantics.

Lovely answered 7/5, 2011 at 5:56 Comment(0)
F
3

THe function arguments in ActionScript pass by value, not by reference. It is absolutely the same as in Java. You can read in details here.

Flinn answered 6/5, 2011 at 22:29 Comment(4)
but this seems to indicate that it should be passed by reference: livedocs.adobe.com/flex/3/html/…Bananas
Ok, to be precise follow my link and you find a description which is fully related to ActionScript too: "Java passes the references by value just like any other parameter. This means the references passed to the method are actually copies of the original references."Flinn
+1 This is the only correct answer so far. The documentation incorrectly uses "pass by reference" to mean "pass by object-sharing" (or "pass by value [of the reference]" for people who like to link in pointers). Please see Evaluation Strategy. The "problem" is the poster expects the real "pass by reference", which is not supported in Javascript or Actionscript.Lovely
(My term "incorrectly" is a tad harsh, however, I stand by the statement that using the overloaded term only adds confusion. The poster makes it clear on several occasions he was expecting the real (or other, if you prefer) "pass by reference" semantics.)Lovely
L
1

The problem I am seeing is

var AC1.addItem(someObject);

Try adding the item within a function.

var AC1:ArrayCollection = new ArrayCollection;
var AC2:ArrayCollection = new ArrayCollection;

addItemToArrayCollection( AC1 );
setAC2(AC1,AC2);
// AC2 should be pointing to the ArrayCollection that AC1 is pointing to.

private function setAC2(_ac1:ArrayCollection, _ac2:ArrayCollection):void
{
   _ac2 = _ac1;
}

private function addItemToArrayCollection( arrayCollection:ArrayCollection ):void
{
  arrayCollection.addItem( someObject );
}

You can add a breakpoint in after the the assignment and see that AC2 should have the same Object as AC1.

Ljubljana answered 7/5, 2011 at 1:49 Comment(1)
This is completely wrong. addItemToArrayCollection( AC1 ); has nothing to do with references. Using your code AC1 has someObject while AC2 is empty, same as topic author's code.Harrold
J
1

I believe that @Constantiner is on the right track, but I think his explanation is lacking detail; so I'm going to try to explain with a bit more depth; as best I understand it. Ya'll can correct me if I'm wrong.

As stated in the docs:

In ActionScript 3.0, all arguments are passed by reference, because all values are stored as objects. However, objects that belong to the primitive data types, which includes Boolean, Number, int, uint, and String, have special operators that make them behave as if they were passed by value.

So, an ArrayCollection is definitely an object, and not a primitive type, so it should be passed by reference and act like it was passed by reference. But, what is your reference variable to the ArrayCollection. Conceptually it just a pointer to some memory space that contains the actual Collection data. Here is my attempt at some ASCII art:

                  |---|
 ac1(variable)--> |   | (ActualArrayCollection1)
                  |---|

                  |---|
 ac2(variable)--> |   | (ActualArrayCollection2)
                  |---|

to repeat, ac1variable is a pointer to some memory space. ac2variable is a pointer to some different memory space. When you pass one of them into a method as an argument it is passed by reference. So, inside the method, you have something like this:

 ac1(variable)--> |---|
 ac1(argument)--> |   | (ActualArrayCollection1)
                  |---|

 ac2(variable)--> |---|
 ac2(argument)--> |   | (ActualArrayCollection2)
                  |---|

So both ac1variable and ac1argument point at the same memory space; because they each contain the same pointer value. However, ac1variable and ac1argument are actually holding different memory spaces. They are not the same.

When the method runs this line:

_ac2 = _ac1;

You get something like this:

 ac1(variable)--> |---|
 ac1(argument)--> |   | (ActualArrayCollection1)
 ac2(argument)--> |---|

 ac2(variable)--> |---|
                  |   | (ActualArrayCollection2)
                  |---|

When the method's execution ends, the two arguments go away, and the original pointer variables remain unchanged. If you want to do a direct assignment like this inside a method, you can access the global variable using the this keyword. This should do it:

   this._ac2 = _ac1;

Of course, that can defeat the purpose of encapsulation inside a method if you're accessing class level variables.

I'm sure an expert on compiler design and such things will eat this for breakfast and spit it up. I hope my ASCII art is consistent across multiple browsers / machines / OSes / etc..

Jehius answered 7/5, 2011 at 2:38 Comment(5)
The documentation is wrong -- it uses "pass by reference" incorrectly (as does the Python documentation) to mean "pass by object-sharing" and there-in lies the crux of this entire "dilemma". So while this post does explain "pass by object-sharing", it doesn't address the posters question :-) The poster is was expecting "pass by reference" (with the correct meaning) to allow modification of the variable passed in, as per C#'s out/ref or VB's ByRef or C++'s (reference) &.Lovely
The this trick is somewhat interesting, but it should be pointed out it will only work if the method if invoked upon the global object (or explicitly upon the object for which AC2 is a member -- I think this.AC2 = _ac1 shows more of the intent; the desire is to change AC2 (albeit through the local parameter _ac2))Lovely
@pst Based on the original poster's code; I have no reason to believe that the the ac2 method and the ac2 variable are not part of the same class; but yes for the 'this' reference to work both the method must be in the class which contains the ac2 variable.Jehius
@pst I'll admit this is the first time I heard the term "pass by object-sharing". As stated in the Wikipeida article: "the term "call by sharing" is not in common use; the terminology is inconsistent across different sources." Do you have a reference for the claims that the documentation is wrong?Jehius
"Wrong" is a little harsh and was used as a hyperbole here; my answer contains more details. I am on a quest to eradicate this usage of "pass by reference" as, with it in play, there is no way to describe the real "pass by reference". Also, I find "pass by reference" (in this context) and "pass by value [of the reference]" to be low-level terms (why should I care about the implementation?) when the precise semantics fit a more-apt term. You are absolutely correct about the terminology being inconsistent, but I think the usage is increasing -- I'm trying, dammit ;-)Lovely

© 2022 - 2024 — McMap. All rights reserved.