这篇文章帮了我很大的忙,转了
Earlier today I fixed a minor code issue. It took seconds to identify, but I remember being a little stumped by a similar problem in my early Flex days. The difference now is that I'm familiar with the component life-cycle.
You may think it is only worth reading up on the Flex component life-cycle if you intend to create advanced components, but that's not true, even when you are doing nothing other than extending basic mxml components a little knowledge goes a long way.
To demonstrate the problem take a look at the init
method for this simple mxml component called TimeInputBar
(which extends Canvas
).
//<code>Init</code> is called when the component's <code>initialize</code> event is fired: public var time:Number; public function init():void { time = 0; }
That's simple enough. Now take a look at the ActionScript which is creating an instance of the TimeInputBar:
var t:TimeInputBar = new TimeInputBar(); t.time = 12; addChild(t);
Again, simple code, but did you spot the problem? With a little bit of knowledge it's easy to see that time
will always be reset to 0 and will never be 12. Why?
When you create a component using the new
operator, only part of the life-cycle completes. The component gets as far as the configuration stage, which means you can set properties on the component (to be processed by the component later) but the life-cycle will not go any further than that. It is in effect, paused.
However, all the really cool stuff in the life-cycle, like creating the component's child elements, measuring the component, drawing the component and dispatching events happens during the next stage.
It is only when you add the component to the display list using addChild
or addChildAt
that the life-cycle continues (in other words ... it continues when the component has a parent).
As mentioned, the next phase is the one in which the cool stuff happens: In more detail, this means that the preinitialize
event is dispatched, the component's children are created, the initialize
event is dispatched and the component's validation methods are called (those are beyond the scope of this article) and if that's not enough for you, when all that is done then everybody's favourite event, creationComplete
is dispatched.
So, with that knowledge in hand let's revisit the order of execution for the original example:
- Create component using 'new' operator
- Set
time
variable to 12 - Add component to display list
- initialize event is fired
init()
method is calledtime
variable is set to 0
Many beginners make the assumption that the preinitialize
and initialize
events are dispatched early on - don't beat yourself up, that's a reasonable assumption to make. However those events may actually be dispatched after you've set initial values for some of your component's properties.
So, what's the solution? We could simply swap the order of our code:
//no worky! t.time = 12; addChild(t); //worky addChild(t); t.time = 12;
...but I'm hoping you can see the issue; components should be just a little more flexible than that!
A perfectly acceptable solution here would be to perform a simple check inside our init method:
public var time:Number; private function init():void { if(isNaN(time)) time = 0; }
[Edit - it's also perfectly acceptable to set 'time' to 0 when you first declare it - this post is not really concerned with which solution you choose, but rather it uses simplified code to explain why such a problem may arise in the first place.]
Once that is done, it will not matter if you set time
before or after you add your component to the display list. If the value is set externally before init is called, the value will not be overridden.
You can read a little more about the Flex component life-cycle in one of my earlier posts here.