Skip to main content
Inspiring
April 7, 2014
Question

Weak Reference support (now and future)

  • April 7, 2014
  • 1 reply
  • 490 views

I built a data-binding system supporting one-way, two-way, and one-time assignment binding.  It's integrated into an XML display list parser that supports construction of any object by using its fully-qualified class name as the tag name or through tag name aliases (e.g. "div" = "flos.gui.GUIControl").  It also supports string attribute value interpretation  to strongly-type objects for pre-defined (object type/attribute name) pairs (e.g. turning "0,0,100,100" into a Rectangle when such a value is found on a DisplayObject's scrollRect attribute in the XML) as well as doing so dynamically for objects that implement an IAttributeParser interface: parse(name:String,value:String):*.

One issue I had to overcome was when using resolvable references for binding such as "<GUIControl name='page' topMargin='{bind this.menuBar.height}'/>" was "late binding", such that resolvable references were deferred to be resolved later, such as in my example where 'this.menuBar', being higher in the display depth, would be created after the 'page' control which flows underneath the menu bar.  The deferred binding works well, and since the infrastructure even supports a constructor child node (with props: 0, 1, 2, etc.) for objects that require constructor parameters, it can also force immediate resolution of attribute values instead of delaying these.

Now, the main issue with this sort of binding system is that when you construct a display list from some XML and it has binding; how should one handle unbinding when display objects go out of scope?  It would be self-defeating to allow binding to be set up so easily in the XML, only to have to force programmers to later lookup and manually unbind those attributes in code for any single object that may be removed from the display list.

I managed to solve that problem by ensuring that my Binding class and BindingDictionary classes (with its source to target and target to source reverse dictionaries) all used Weak References to the source objects.  The Dictionaries which used the source objects as keys for the target object entires were simply instantiated by passing true to the constructor to use weak references for keys.  The Binding and TargetSourceProperty instances which had a "source" property and the "PropertyReference" class which stored a base object and target property name, had to be updated to use a WeakReference for their "source" and "base" properties.  Now, once the Binding is established, it allows (i.e. does not prevent) the source object to be garbage collected, the source dictionary entry vanishes automatically when the the source object is garbage collected, and if the source happened be a target as well, the entry is removed when it detects the target has been garbage collected (e.g. the WeakReference is discovered to be null when it attempts to assign a value to the target).  It all works wonderfully.

I implemented a WeaKReference like so:

     public class WeakReference
     {
          private var d:Dictionary;
          
          public function WeakReference( obj:* )
          {
               if (obj == null)
                    throw new Error( "WeakReference target object cannot be null." );
               d = new Dictionary( true ); //use weak keys
               d[obj] = 0; //store obj as weak key with dummy content
          }
          
          public function get():*
          {
               for (var key:* in d) //if target object was garbage collected, dictionary will contain no keys since keys were weak references
                    return key;
               return null;
          }
     }

It just seems like such a hack to have to use a whole Dictionary object to store one weak reference.  Is there any other way to store a weak reference?  Are there any plans to add better support for weak references in ActionScript?

As I've demonstrated, they are very important to this kind of system, where one must have the ability to establish bindings in XML, which will instanate objects that can be discarded without worrying about having to manually unbind everything to avoid memory leaks.

This topic has been closed for replies.

1 reply

Amy Blankenship
Legend
April 7, 2014

I wouldn't bother with weak references--IME they almost never do what I need them to do. Instead, listen for REMOVED_FROM_STAGE in capture phase and clear references there or override addEventListener such that you keep a reference to all the listeners that were added so they can be removed.

Inspiring
April 8, 2014

Actually, weak references work perfectly.  As I explained, I already solved the problem and was simply asking whether anyone knows of another way to use them or whether Adobe plans to add better support for them, since they are so important.

The nice thing about weak references is that I am able to test whether they are working.   By creating weak references to objects (including those on stage) which are participating in binding, I know for certain when they have been garbage collected when they are supposed to, because their weak references are lost.  I can tell you that it absolutely works as expected.  People who can't get them to work probably aren't using them right, and probably aren't clearing all the references to an object.  It's especially important to use weak references with event listeners, but I tend to use event signals instead anyway, which are more robust in that I can control the exact order in which the signals are fired and I can easily remove specific handlers.

Listening to the REMOVED_FROM_STAGE event is a very naive approach, and I don't mean that in an insulting way, but simply that it's not an approach that can be used effectively in this situation, nor is it even necessary since I already have the system working as expected.  As I explained: "It would be self-defeating to allow binding to be set up so easily in the XML, only to have to force programmers to later lookup and manually unbind those attributes in code for any single object that may be removed from the display list."  To clarify, the entire display list is constructed from XML (e.g. similar to how MXML, XAML, and XHTML work).  Arbitrary attributes can be bound in the XML.  The BindingDictionary tracks every object that is bound, but unless it uses weak references for a generic implementation that can handle the removal of any object at any time, the programmer would have to decide (i.e. write special code) when they are done using any particular object, they'd have to see whether it's present in the binding dictionary, then they'd have to make sure they remember to unbind it.   It's just much more effective the way I've done it, because objects can just be removed and forgotten about; the binding dictionary won't prevent them from being garbage collected.

So, firstly, removal from the stage does not necessarily mean that the object is done being used.  Secondly, Binding to arbitrary properties on arbitrary objects, may include objects that aren't even display objects.  I can bind to any property on any object that implements my IPropertyWatch interface, and my ObservableObject base class offers a default implementation for ease of use.  Finally, I'm not using addEventListener at all in this implementation.