View Single Post
Old 05-31-2006, 02:09 AM   #19
senocular
six eyes
 
senocular's Avatar
 
Join Date: Jan 2003
Location: San Francisco, CA (USA)
Posts: 7,875
Send a message via ICQ to senocular Send a message via AIM to senocular Send a message via MSN to senocular Send a message via Yahoo to senocular
Default

Oddities in Component Removal

Flash components are special movie clips in Flash that have an added level of functionality provided to them through ActionScript. The ActionScript that allows for this is organized into what is known as a class. A class is a self-contained collection of code that serves a single purpose such as defining all the unique features of a component. When components are created, the class is "constructed" and all of that extra functionality is added to the movie clip that assumes the role of the component.

Some components in Flash require a "deconstructor" – something that allows a component movie clip to clean up some variables or remove related movie clips or components when they themselves are removed from the screen. For example, ComboBox components in Flash are actually made up of 2 components, a ComboBox base and a drop-down List component that appears when the ComboBox is clicked. These two components are not encased in one component timeline but exist as separate components in the timeline in which the ComboBox is placed.

To ensure that when the ComboBox component is removed, the List component that is associated with the ComboBox base is also removed, the base component uses an onUnload event within its ActionScript class to explicitly remove the respective List. The onUnload event is an event for movie clips that is invoked when a movie clip is removed from its parent timeline.

The onUnload event in Flash does not work immediately following the actual removal of the object in which it is associated, however. It waits for the end of the current frame for objects removed with ActionScript and the beginning of following frame for objects removed via the timeline—and this is just by design of the Player; there is no way around it. Due to this timing, in order for the onUnload event to remain active, it requires that the object being removed remain in memory until that event fires-and by memory I mean it will remain accessible through ActionScript despite the fact that the actual movie clip instance on the timeline has been physically removed. This is necessary because Flash's garbage collection (the mechanism by which Flash internally deletes variables and values from memory). If in ActionScript there remained no reference to the instance of the component in code, the onUnload event would be cleaned up by garbage collection and deleted before it has a chance to fire. The reference that Flash will use to keep this onUnload event in is the instance name of the movie clip or component being removed.

Note: the Flash V2 control components that utilize the onUnload event include:
  • RadioButton
  • DateField
  • ComboBox
  • MediaDisplay (and MediaPlayback as it contains a MediaDisplay)


Movie Clip/Soft References

Movie clip references in Flash, unlike other object references (i.e. objects existing only in ActionScript) are soft references. Soft references reference an object but do not enforce whether or not that object persists in memory. Flash uses soft references for movie clips because the removal of a reference in code is not allowed to be responsible of the physical removal of a movie clip from the timeline. Instead, one should use removeMovieClip. Similarly, a movie clip can be removed from a timeline (using removeMovieClip) and obsolete references can be left behind.

In Flash, these soft movie clip references recognize movie clips on the timeline using their absolute path. Even if a movie clip is removed, a reference can remain and continue to reference that same path despite the fact that nothing exists in that path. However, if you create any other movie clip that matches that path, the original reference will pickup and reference that new object. Example:

ActionScript Code:
// With Button and Label components in the library var ref = createObject("Button", "myComponent", 1); trace(ref); // _level0.myComponent destroyObject("myComponent"); trace(ref); // [nothing] createObject("Label", "myComponent", 2); trace(ref); // _level0.myComponent

The variable ref will continue resume reference to the new Label component because it shared the same path as the Button component it initially was assigned to.

Another unique aspect about movie clips objects is that they can potentially share the same name in code. Movie clips have instance names which are translated into variable references in ActionScript but it is not essentially required for them to be unique. Of course you'd want them unique because if they were not, you would only be able to access one of those similarly named movie clips through ActionScript. The others would still be there, just not accessible. The movie clip that the instance name would reference is that which is at the lowest depth. So if you had two components each with the name "myComponent" the movie clip reference myComponent would reference the component with the lowest depth. Example:

ActionScript Code:
// With Button and Label components in the library createObject("Button", "myComponent", 1); createObject("Label", "myComponent", 2); trace(myComponent.getDepth()); // 1

Note: variable references to movie clips will correctly reference the correct movie clip despite similar instance names among movie clips

References and onLoad

Now what happens when you try to remove a component that has an onUnload event? First, you may notice that the component is successfully physically removed from the screen. But, as a result of the onUnload's necessity to avoid garbage collection, the component object remains in memory and is therefore still accessible in code. This will last until the very end of the current frame (or the beginning of the next frame if the component was removed via the timeline). That means any script that continues to run in the current frame following this removal will still have access to that component despite its ultimate demise at the end of the frame.

Now, what happens if you want to create a new object of the same name? Then you have two instances with similar instance names (despite one is only in memory and will be gone at the end of the frame). But what this also means is that your new reference to the supposedly new movie clip or component will now continue to reference the removed object that exists only in memory. Why? Because the depth of the removed object shoots down to some ridiculously low value after removal since it technically no longer exists. At such a low level, it puts it below any new movie clip created and thereby assumes control the instance name reference.

ActionScript Code:
// With ComboBox component in the library createObject("ComboBox", "myComponent", 1); destroyObject("myComponent"); trace(myComponent.getDepth()); // -32770 createObject("ComboBox", "myComponent", 1); trace(myComponent.getDepth()); // -32770

Note: this reference transcends parent movie clips so you may find removing movie clips containing these problematic components experience the same problems.

Solutions

One solution to this problem is to wait until after the end of the frame to start checking for or adding new movie clips or components that use the same name as those removed (or movie clips that contained components of this nature) so the onUnload can complete itself and the object in memory can be completely removed. This can be achieved using onEnterFrame or setInterval/setTimeout.

ActionScript Code:
// With ComboBox component in the library createObject("ComboBox", "myComponent", 1); destroyObject("myComponent"); trace(myComponent.getDepth()); // -32770 onEnterFrame = function(){     createObject("ComboBox", "myComponent", 1);     trace(myComponent.getDepth()); // 1     delete onEnterFrame; }

Another option is to avoid using the same instance name over again for these components or their containers. By doing so, you never have a conflict and won't have to worry about references getting confused. Additionally, it helps if you use variables to keep track of your components instead of the instance names. This prevents references to those objects appearing to exist even though they were removed (only instance name references continue to reference the movie in memory for the onUnload event).

ActionScript Code:
// With ComboBox component in the library var myComponent = createObject("ComboBox", "component1", 1); destroyObject("myComponent"); trace(myComponent.getDepth()); // undefined myComponent = createObject("ComboBox", "component2", 1); trace(myComponent.getDepth()); // 1

Aside from that, what you can do is manually change the instance name of the component being removed to something unique when you remove it (as opposed to when you create it). This way, the only reference it uses is one linked to that new, unique name which shouldn't be referenced anywhere in your code even if you stuck to using the instance name originally provided for the component at the time of its creation.

ActionScript Code:
// With ComboBox component in the library createObject("ComboBox", "myComponent", 1); destroyObject("myComponent"); myComponent._name = "unloadComponent1"; trace(myComponent.getDepth()); // undefined createObject("ComboBox", "myComponent", 1); trace(myComponent.getDepth()); // 1
__________________
(6)

Last edited by senocular; 05-31-2006 at 03:06 PM.
senocular is offline   Reply With Quote