LeakEvent → WeakEvent

01/02/2012

I’m sure not any Silverlight developer is aware of the fact that when one subscribes to an event, a strong reference to the handler class (‘target’) is attached to the event source (‘source’), hence, if the source object life-cycle is longer than the target, memory is leaked, becuase when you don’t need the target anymore and all its references are removed, there is still one reference to it by that event handler in the source, and thus, won’t get collected by GC.

A strong reference is a normal reference that protects the referred object from collection by a garbage collector. The term is used to distinguish the reference from weak references.

If you’re using Silverlight you must have encountered this scenario before.
Here is a typical scenaio: you have long running object publishing events and a temporary view model that handles its events, once you subscribe to an event of the long-running object, you may never get rid of the view-model until that object itself is GCed.
Say you have a LoginViewModel that subscribes to WebContext.Authentication.LoggedIn event (WCF RIA) within your view model, when your view-model is supposed to go out-of-scope, it won’t be eligible for GC since the LoggedIn event source holds a reference to it! This is just an example that its impact might be low considering the LoginViewModel and its resources (no memory leak is ever considerable at all but anyway), if we’re talking about a utility that holds heavyweight info and subscribes to events published by a long-living object without letting the GC collecting itself (by weakly subscribing to that event) is a terrible memory leak.

To solve this problem, Microsoft provides the WeakReference wrapper which is used as a proxy to an object, and references an object while still allowing that object to be reclaimed by garbage collection.

Unlike in Silverlight, other platforms (i.e. WPF) have out-the-box support for weak events, however, those helpers are not shipped with Silverlight.
Another issue is, invoking a private method by reflection is restricted in Silverlight, and that’s why the provided WeakHandler class bellow doesn’t take a method for self maintenance.

I won’t repeat the basics, I will just refer you to an awsome article written long time ago, that goes in detail of this issue, and actually influenced my code as well.
I expanded the code in the article to overcome the private method invoking restriction, so the ‘target’ has to implement a handler method, then all you have to do is remove the handle at destruction (will reach destructor since we use a weak-reference that will allow GCing the target.

I named it WeakHandler to keep it simple, here is what the class looks like (usage bellow):

namespace System
{
 
  /// <summary>
  /// Encapsulates an event publisher and a handler while still allowing the objects to be garbage collected.
  /// </summary>
  /// <typeparam name="TTarget">The type of the class that declares the handler.</typeparam>
  /// <typeparam name="TEventArgs">The type of <see cref="EventArgs"/> this event handler accepts as an argument.</typeparam>
  public class WeakHandler<TTargetTEventArgs>
    where TTarget : classIWeakHandler<TEventArgs>
    where TEventArgs : EventArgs
  {
 
    private WeakReference _target;
 
    /// <summary>
    /// Initializes a new object of <see cref="WeakHandler"/>.
    /// </summary>
    /// <param name="target">The object that handles the event.</param>
    public WeakHandler(TTarget target)
    {
      if (target == nullthrow new ArgumentNullException("target");
 
      _target = new WeakReference(target);
    }
 
    /// <summary>
    /// The method that should actually be subscribed to the event.
    /// </summary>
    /// <param name="sender">The object that publishes the event.</param>
    /// <param name="e">The <see cref="EventArgs"/> to be passed to the event handler.</param>
    public virtual void OnEventRaised(object sender, TEventArgs e)
    {
      if (Target != null)
        Target.OnEventRaised(sendere);
    }
 
    /// <summary>
    /// The object that handles the event.
    /// </summary>
    public TTarget Target
    {
      get
      {
        return (TTarget)_target.Target;
      }
    }
 
    //An empty destructor costs performance (see this for detail: http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx)!
#if DEBUG
    //Will Validate our test:
    ~WeakHandler()     {

     System.Diagnostics.Debug.WriteLine("WeakHandler finalized.");     }
#endif   }
  /// <summary>
  /// Defines an event handler of the specified <see cref="EventArgs"/>
  /// </summary>
  /// <typeparam name="TEventArgs">The <see cref="EventArgs"/> type of this event.</typeparam>
  public interface IWeakHandler<TEventArgs>
  where TEventArgs : EventArgs
  {
    /// <summary>
    /// The method to be raised when the event is fired.
    /// </summary>
    /// <param name="sender">The object that raised the event.</param>
    /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
    void OnEventRaised(object sender, TEventArgs e);
  }

 

}

Usage:

public partial class App : Application
{
 
  public App()
  {
    InitializeComponent();
    Startup += Current_Startup;
  }
 
  void Current_Startup(object senderStartupEventArgs e)
  {
    Source source = new Source();
    Target target = new Target(source);
    source.RaiseEvent();
    target = null;
 
    Debug.WriteLine("Collecting Target.");
    GC.Collect();
    GC.WaitForPendingFinalizers();
 
    Debug.WriteLine("Recollecting to clean weak handler.");
    GC.Collect();
    GC.WaitForPendingFinalizers();
 
    source.RaiseEvent();
    source = null;
 
    Debug.WriteLine("Collecting Source.");
    GC.Collect();
    GC.WaitForPendingFinalizers();
 
    /*
    Output:
      Raising event.
      Event raised.
      Collecting Target.
      Finalizing Target.
      Target finalized.
      Recollecting to clean weak handler.
      WeakHandler finalized.
      Raising event.
      Collecting Source.
      Source finalized.
      */
  }
}
 
public class Target : IWeakHandler<EventArgs>
{
  WeakHandler<TargetEventArgsweakHandler;
 
  public Target(Source source)
  {
    _Source = source;
    weakHandler = new WeakHandler<TargetEventArgs>(this);
    Source.Event += weakHandler.OnEventRaised;
  }
 
  private readonly Source _Source;
  public Source Source
  {
    get
    {
      return _Source;
    }
  }
 
  public void OnEventRaised(object senderEventArgs e)
  {
    Debug.WriteLine("Event raised.");
  }
 
  ~Target()
  {
    Debug.WriteLine("Finalizing Target.");
    //Omitting the following line will cause the WeakHandler to have a life-time as the source.
    //Although the WeakHandler is just a body-class that holds nothing
    //(remember that target is weak-referenced hence can be garbage collected).
    Source.Event -= weakHandler.OnEventRaised;
    Debug.WriteLine("Target finalized.");
  }
}
 
public class Source
{
  public event EventHandler Event;
 
  public void RaiseEvent()
  {
    Debug.WriteLine("Raising event.");
    if (Event != nullEvent(thisEventArgs.Empty);
  }
 
  ~Source()
  {
    Debug.WriteLine("Source finalized.");
  }
}

You can download the source code from here, or the DLL from here.

Any comments, improvements, or if you just like to shout out for fun, anything will be welcomed!

HTH,
Shimmy

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>