Skip navigation
Currently Being Moderated

Browsing memory or find out what []@f5a6251 is that it can't coerce to ArrayCollection

Nov 23, 2012 8:31 AM

Whenever I run my program, the console gets spammed w/

TypeError: Error #1034: Type Coercion failed: cannot convert []@f5a6251 to mx.collections.ArrayCollection.

and I'd like to do something about that. Unfortunately, I can't find []@f5a6251. So how do I? I have no idea which code executes this.

 
Replies
  • Currently Being Moderated
    Nov 28, 2012 4:03 PM   in reply to JackHombre

    I know, I know. Irritating when those happen...

     

    If the function was called in your code directly, or by a defined function / object, the compiler would do type checking pre-compile and would give you a warning to check in FlashBuilder.

     

    But...

     

    There are a number of ways this can be bypassed. For example, it happens a lot with Binding where the calls use anonymous functions and are error-trapped in MXML in any case (so errors frequently fail to surface). I find they also occur when dispatching events with similar names and different types.

     

    This type of error is a pig to debug but it is _possible_ to debug. Especially with FlashBuilder.

     

    So...

     

    The laborious way that will guarantee a fix:

     

    What you need to do is to fire up flash builder and open your global search box (Ctrl+H in FB4.6, I think it was something else in earlier FB).

     

    1. Search for arguments defined in your function signatures with datatype ArrayCollection.

    EG: Search for ":*ArrayCollection" to recognise any occurrence of a type declaration of ArrayCollection

    2. Disregard any search results that are _not_ in a function signature

    3. What is left are all the candidates for debug, eg: you should have a list of functions like:

    protected function problemFunction(anArgument:ArrayCollection):void { }

     

    You then just need to look through your list of functions and find any that are called dynamically by asynchronous calls or that are called with untyped data, or that are called by bound calls (label="{calculateSomeStuff(value,value)}" in MXML).

     

    If that doesn't work, simply change the data types of the ArrayCollection function arguments to "Object" or "*" and then either put a break point or throw an error when the data is not an ArrayCollection - either way the FB debugger will stop you on that line of code and allow you to see what called it and why.

     

    The quickest and easiest way that will guarantee a fix if you do it in time

    Of course, the quickest and best way is to test you code frequently and, if this error occurs, to remember what you just changed to introduce the error... and undo that change!

     

    Cheers,

    G

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 29, 2012 10:01 AM   in reply to JackHombre

    TBH, there are more ways than that, but those seemed the most likely.

     

    The thing is, when you have compiled code with properly typed variables, it's very easy for the compiler to see problems and give you a warning - 1067: Implicit coercion of a value of type {whatever} to an unrelated type mx.collections:ArrayCollection. Because you aren't seeing the error 1067, it means the situation isn't explicitly stated in your code at compile time, so must be something that occurs at runtime, when the code is evaluated.

     

    These are the possibiliies:

     

    You have a function like this:

    function myFunction(value:ArrayCollection):void { }

    And something calls it like this:

    myFunction(somethingThatIsNotAnArrayCollection);

     

    Alternatively, you could have something like this:

    package

    {

              import mx.collections.ArrayCollection;

      public class MyClass

              { 

    public function MyClass(ac:ArrayCollection) {                    }

    }}

    And something could try to instantiate it like this:

    var ref:MyClass = new MyClass(somethingThatIsNotAnArrayCollection);

     

    Finally, you could have something like this:

    var ac:ArrayCollection = somethingThatIsNotAnArrayCollection;

     

    And to avoid the compiler locating these prior to compile time, you need to have ways for that to occur that bypass the pre-compile type checking.

     

    For example;

     

    var a:Array = someData;

    for(var i:int=0;i<a.length;i++) {

         var ac:ArrayCollection = a[i]; // no way for compiler to know what type a[i] is so it gives the benefit of the doubt

    }

     

    Or...

    var ac:ArrayCollection = myObject['propertyNameThatRefersToANonArrayCollection'];

     

    Or...

    callLater(myFunction,[aValueOfTheWrongDatatype]);

     

    Or...

     

    You get the gist.

     

    It's a horrid, horrid, horrid bug to have to resolve and I've had one or two too many of them already.

     

    FYI, there is _another_ way for something similar to happen that I had forgotten about, but which only occurs under very specific conditions... Sometimes, when loading a pre-compiled movieclip into another that is running debugger and interacting via code, you get some weird memory space issues so that valid objects in both give a type error like cannot coerce MyObject@[1234] to MyObject or similar... There may be some cross domain stuff happening too, but it disappears when both files are properly compiled for release.

     

    G

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 29, 2012 10:04 AM   in reply to Gaius Coffey

    PS: Another way I've seen this happening is with metadata declarations... (Though those are usually for events, not for data types.)

     

    EG:

    [Event(name="bob",type="flash.events.BobEvent")] is defined in MXML but the event that is dispatched is a plain and simple Event so type checking fails.

     

    Similarly, if you have several handlers for similarly named events, you sometimes get in trouble if they are different event types.

     

    G

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 30, 2012 7:01 AM   in reply to JackHombre

    Cool, glad you got to the bottom of it!

    G

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 30, 2012 1:34 PM   in reply to JackHombre

    Ah, now that's different...

     

    A binding failure is stating that the property you are referring to is not declared as [Bindable]. Which means that Flex will populate it with the value it has when it comes across it and _never_ update it. Which means, if you instantiate the control and update the thing you've bound, it won't be updated.

     

    And that's bad.

     

    Essentially: (myValue as ArrayCollection).length is identical to aFunctionThatReturnsValueAsAnArrayCollection(myValue). However, because the compiler sees a function rather than a declaration, it treats it a little differently. But the underlying problem is _still_ there.

     

    This will manifest as difficult to trace bugs where code appears to be working, but will frequently display out of date info or fail to respond to things that it absolutely should be responding to. That's the trouble with binding in general; it is so easy to use and so fast that you use it for everything, but if you get it wrong, it has no error checking / reporting / notification so it is really tough to trace the problem.

     

    There are a number of ways to resolve the issue that would be better (as in, they might actually _fix_ the underlying issue).

     

    1. You could modify the property in the class that declares it to be [Bindable] (if it's a read-only property, you can tie the binding into a relevant event, for example, you could use [Bindable("dataProviderChange")] to update bindings whenever a new Event("dataProviderChange") is dispatched.

     

    2. You could set a bound variable on your own class to the dataProvider and bind _that_ instead.

     

    3. You could set a bound int to the length when the dataProvider is set...

     

    4. Something else...

     

    But, and this is the big thing... kill that DataProvider2ArrayCollection function because it is just hiding a problem that you would much rather have fixed!

    G

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 30, 2012 1:43 PM   in reply to Gaius Coffey

    ps: For clarity;

    When I say "set a bound variable" on your class, I mean you need to find a way to set the variable whenever the underlying variable changes...

    Typically, I would use a getter / setter for this so that when I set the relevantControl property, it will update a number of other relevant properties. That's fine, provided the underlying properties are not going to change, but if they are, then you also need to add listeners to keep your bound variables up to date.

    Definitely much easier and more robust if the underlying control is declared as [Bindable] and that binding is properly maintained by the underlying control.

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 4, 2012 3:25 PM   in reply to JackHombre

    JackHombre wrote:

     

    I don't think I quite follow. What gets assigned to another_control.dataProvider is always an Array or an ArrayCollection.

    Yes, the binding warning is not the same as the data type warnings.

     

    Binding warnings are saying that a value hasn't been declared as [Bindable] and so Flex doesn't know how to keep it updated.

     

    EG:

    A time sequence might be like this:

    1. myComponent is instantiated, Flex sees value of aControl.dataProvider bound to myProperty so it sets myProperty = aControl.dataProvider

    2. Some time later, aControl.dataProvider is replaced by a new value, but aControl.dataProvider is not declared as [Bindable] so this change is not propagated to myComponent

    3. myComponent is now showing stale data and there is no way to refresh it

     

    What you want instead is:

    1. myComponent is instantiated, Flex sees value of aControl.dataProvider bound to myProperty so it sets myProperty = aControl.dataProvider

    2. Some time later, aControl.dataProvider is replaced by a new value, aControl.dataProvider is  declared as [Bindable] so this change is  propagated to myComponent

    3. myComponent is now showing current data, everybody is happy

     

    There are thousands of different approaches to achieving the latter.

     

    For example:

    [code]

    public function get boundControl():ThirdPartyComponent { return _boundControl; }

    private var _thirdPartyComponent:ThirdPartyComponent;

    public function set boundControl(tpc:ThirdPartyComponent):void {

         if(_thirdPartyComponent) // If an old setting is found, clean up first

              setThirdPartyListenersAndProperties(_thirdPartyComponent,false);

         _thirdPartyComponent = tpc;

         if(_thirdPartyComponent) // If a new setting is found, listen for changes

              setThirdPartyListenersAndProperties(_thirdPartyComponent,true);

    }

    protected function setThirdPartyListenersAndProperties(tpc:ThirdPartyComponent,listening :Boolean):void {

         if(listening) {

            // Code to listen for changes that would invalidate bound properties, such as new data received that

           // overwrites the dataProvider property with an entirely new object

           ...

            // Set bound properties

           var ac:IList = tpc.dataProvider as IList;

           if(!ac && tpc.dataProvider && tpc.dataProvider is Array) ac = new ArrayCollection(tpc.dataProvider);

           listenedTPCData = ac;

          } else {

            // Code to remove listeners and clean up for garbage collection

           ...

            // Code to clear bound properties

          listenedTPCData = null;

         }

    }

    // Probably overkill, but your comments above suggest data may be Array rather than ArrayCollection

    // so should probably be treated differently to ensure data is valid. I've used datatype IList in case

    // it is an XMLListCollection or some other collection variant instead

    [Bindable] private var listenedTPCData:IList;

    [/code]

     

    Also, bear in mind that this binding must be valid all along the chain. For example, if I bind to myControl.dataProvider.length, all three properties this.myControl, myControl.dataProvider and dataProvider.length must be declared as [Bindable] for Flex to follow changes. It doesn't matter that IList.length and myControl properties are bindable if the dataProvider property of myControl is _not_ bindable, the binding will not update if the dataProvider reference is changed.

    G

     
    |
    Mark as:

More Like This

  • Retrieving data ...

Bookmarked By (0)

Answers + Points = Status

  • 10 points awarded for Correct Answers
  • 5 points awarded for Helpful Answers
  • 10,000+ points
  • 1,001-10,000 points
  • 501-1,000 points
  • 5-500 points