View Full Version : Getting rid of null object references.
charlesshoults
02-13-2009, 04:31 PM
I'm trying to go through my code and replace as many mxml constructs as I can with equivalents built in as3. The whole business of referencing objects before the system knows they exist is really aggravating. Within my application, I'm doing the following:
Set up the canvas inside variable declarations: "[Bindable] public var baseLayer:Canvas;"
Designate the starting function within the application line: 'creationComplete = "init();" '
Begin defining properties of the component: "baseLayer.x = 0;"
When the application is run, the line baseLayer.x = 0; throws the error. TypeError: Error #1009: Cannot access a property or method of a null object reference.
I thought maybe that even though it's defined in variables, it hadn't been added to the application yet so I tried application.addChild(baseLayer); but it didn't make a difference.
For those of you who primarily work in AS3 and not mxml, how do I fix this?
charlesshoults
02-13-2009, 06:36 PM
Well, the fix was pretty simple when I got to it.
Instead of saying:
[Bindable] public var baseLayer:Canvas;
I have to say:
[Bindable] public var baseLayer:Canvas = new Canvas;
Sly_cardinal
02-14-2009, 08:27 AM
There are a number of things going on here so this might get a bit complex.
Whilst your solution of initialising the variable inline solves the problem in this particular case, it also violates some of the rules of the Flex component lifecycle.
The component life cycle is the framework that is provided by Flex to enable all sorts of things, like deferred object instantiation, to keep your application efficient and responsive.
The idea is that certain actions (such as instantiating children) are deferred until they need to be done.
In this case, the baseLayer should be instantiated in actionscript (as opposed to MXML) in the 'createChildren' method.
[Bindable]
public var baseLayer:Canvas;
override protected function createChildren():void
{
super.createChildren();
baseLayer = new Canvas();
// Set your properties on baseLayer here.
addChild(baseLayer);
}
That way, by the time your 'creationComplete' handler is invoked your Canvas is ready to use.
Here is a link that provides a step-by-step overview of the flex component lifecycle:
http://flexcomps.wordpress.com/2008/05/09/flex-component-life-cycle/
Things get a little more complicated from here as you start needing to use property getters/setters and the 'invalidateProperties', 'invalidateDisplayList' , 'commitProperties' and 'updateDisplayList' methods to manage component interaction - but I'll leave that for a later post.
charlesshoults
02-17-2009, 04:45 AM
When you set up objects using MXML, does everything basically get built up at the same time? Actionscript makes sense that things would be put in play in the order in which they're coded but in MXML, it seems like it would work more like HTML, having to wait on everything to populate. I would think that perceived speed of actionscript would be more responsive but it also makes things a little more difficult to design, losing some of the layout capabilities of Flex Builder.
Sly_cardinal
02-17-2009, 09:28 AM
It depends - there is an order of instantiation that you can follow.
Children are instantiated in the order in which they are defined and all children complete their initialisation process before their parent's is complete (as initialising children is part of the process...).
I think it's more about knowing when you can interact with particular objects.
e.g. Imagine we have an MXML component called ChildContainer that defines a number of children in MXML.
There is a property called 'childX' that we can set at design-time through MXML. This property sets the x position of the first child of the container:
<ChildContainer childX="50" />
A naive implementation of this property might define it like this:
// Sets the x position of the first child.
public function set childX(value:Number):void
{
child1.x = x;
}
public function get childX():Number
{
return child1.x;
}
However this will fail as the property is set on ChildContainer before it has had a chance to instantiate its children.
Instead we need to wait until we *know* the children have been created and are able to have their properties changed.
The solution is to work with the flex framework - we take note of the value that is being set in the private '_childX' property. We indicate the value has changed and invalidate the display list.
Before flex re-renders the screen the 'updateDisplayList' function is called. One of the nice things about flex is that we are *guaranteed* to know that all of the children have been initialised by the time we enter this function. This means that we check to see if any properties have changed (e.g. the childX property) and then update the value on our child.
Here is a good overview of the different invalidation functions:
http://livedocs.adobe.com/flex/3/html/help.html?content=ascomponents_advanced_3.html
private var _childX:Number = 0;
private var childXChanged:Boolean = false;
// Sets the x position of the first child.
public function set childX(value:Number):void
{
_childX = value;
childXChanged = true;
invalidateDisplayList();
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if (childXChanged)
{
child1.x = _childX;
childXChanged = false;
}
}
It seems a little unwieldy for such a trivial example
|
vBulletin® v3.8.4, Copyright ©2000-2009, Jelsoft Enterprises Ltd.