Using true weak actions without causing memory leaks

by Geert 29. December 2011 16:01

Recently I needed weak actions for the message mediator implementation of Catel. I looked at a few examples and most seem to do something like this:

   1: public class WeakAction
   2: {
   3:     private readonly Action _action;
   4:     private WeakReference _reference;
   5:     
   6:     public WeakAction(object target, Action action)
   7:     {
   8:         _reference = new WeakReference(target);
   9:         _action = action;
  10:     }
  11:     
  12:     public bool IsAlive
  13:     { 
  14:         get { return (_reference != null) ? _reference.IsAlive : false; }
  15:     }    
  16:     
  17:     public void Execute()
  18:     {
  19:         if (_action != null && IsAlive)
  20:         {
  21:             _action();
  22:         }
  23:     }    
  24: }

There are 2 downsides of this approach:

  1. It doesn’t allow static handlers
  2. It causes memory leaks

The problem

So if you are using such an implementation of a weak action, all your alarm bells should be ringing right now. The issue is in the storage of the Action object. The action is a method handler. To be able to call the handler, it must store the reference (as Target property on the Action class) of the target.

However, this means that there is now a reference to the target, so the target can never be garbage collected, so you are now dealing with a big issue in your application because you think that you are using weak events, but you aren’t.

The Solution

I already wrote a true weak event listener once, so this one should be easy. What you basically need to do is to write an open instance delegate, create a handler without a target for it and late-bind the handler. Sounds complex, but once you get the grip of it, it’s pretty simple. Below is the code for a non-generic Action. All this code (and the generic implementation) can be found at http://catel.codeplex.com.

   1: public class WeakAction
   2: {
   3:     public delegate void OpenInstanceAction<TTarget>(TTarget @this);
   4:     private Delegate _action;
   5:     private WeakReference _reference;
   6:     
   7:     public WeakAction(object target, Action action)
   8:     {
   9:         _reference = new WeakReference(target);
  10:         
  11:         var targetType = (target != null) ? target.GetType() : typeof(object);
  12:         var delegateType = typeof(OpenInstanceAction<>).MakeGenericType(targetType);
  13:  
  14:         _action = Delegate.CreateDelegate(delegateType, null, action.Method);        
  15:     }
  16:     
  17:     public bool IsAlive
  18:     { 
  19:         get { return (_reference != null) ? _reference.IsAlive : false; }
  20:     }    
  21:     
  22:     public void Execute()
  23:     {
  24:         if (_action != null && IsAlive)
  25:         {
  26:             _action.DynamicInvoke(_reference.Target);
  27:         }
  28:     }    
  29: }

Tags: ,

C# | Catel

Comments are closed

About the Author

Geert van Horrik is an independent freelance software developer since January 1st, 2007. Since then he was been working on several projects from C++ to C# (WPF, Silverlight, ASP.NET, etc). Currently he loves to write his software using WPF (or Silverlight if WPF isn't an option).

Lately, Geert is spending a lot of time on Catel, a free open-source MVVM Framework for WPF and Silverlight. Actually, it's more than "just" an MVVM Framework, it's a complete application library!