WPF WeakReference and GC

1.2k views Asked by At

I am looking at a mediator prototype here https://joshsmithonwpf.wordpress.com/2009/04/06/a-mediator-prototype-for-wpf-apps/.

The author indicates that "My first thought was to store a reference to the Action in a WeakReference. Since the Garbage Collector will throw away objects that are referenced only by WeakReference objects, it would seem that this would do the trick. Unfortunately, it’s not that simple. The problem with that approach is that the GC will throw away the Action instance because it is only being referenced by a WeakReference!"

My question is instead of using "a reference to Action", why "a reference to MethodInfo" do the trick? I assume the methodinfo should be collected as well.

Thanks in advance.

1

There are 1 answers

2
CodingYoshi On

My question is instead of using "a reference to Action", why "a reference to MethodInfo" do the trick? I assume the methodinfo should be collected as well.

No, it is not really MethodInfo vs Action that does the trick. It is something else but I have to admit, that article is not very well written and can be confusing. Let me try and explain.

Events and GC

Let's say you have a class with an event called Click. The class is: Button Let's say you have another class which subscribes to the event from Button like this:

public class Subscriber1
{
    public Subscriber1(Button button)
    {
        button.Click += ClickHander;
    }

    private void ClickHander(object sender, EventArgs e)
    {
        // ...
    }

    public void UnSubscribe()
    {
        button.Click -= ClickHandler;
    }
}

Now how does the button class know who to notify when the button is clicked? Well, it keeps a reference to all those classes that have subscribed. So in the case above it will hold a reference to an instance of the Subscriber1 class. Whenever, the button is clicked it will notify instance of Subscriber1. So what will happen if we do this:

subcriber1Instance = null;

What will happen is that if the button instance is still alive, the GC will not collect subscriber1Instance. Why? Because it is rooted: the button instance is holding a reference to it. This is why we should do this instead:

subscriber1Instance.UnSubscribe();
subcriber1Instance = null;

In reality Subscriber1 should implement IDisposable and do the unsubscribing there but the point of this answer is not that. So I am keeping it simple so we do not lose focus.

When we unsubscribe from the Click event of the button instance, then the subscriberIntance is no longer rooted and it can be cleaned up by the GC.

So what is the article doing?

In that article, the authors are trying to solve this issue of developers forgetting to unsubscribe and thus leading to memory leak issues. Note this is one way: only if the publisher outlives the subscriber, it will keep the subscriber alive, not the other way around. So if the publisher is ready for GC, the subscriber will not be able to keep it alive.

Basically what the authors are saying is that the subscriber should not subscribe to the event directly so it does not result in the button holding a hard reference to it. Instead the subscriber should derive from WeakReference and pass that to the button when subscribing. Like this:

private class WeakSubscriber1 : WeakReference
{
    public WeakSubscriber1(Subscriber1 target) : base(target) { }

    public void ClickHander(object sender, EventArgs args)
    {
        Subscriber1 b = (Subscriber1)this.Target;

        if (b == null)
        {
            Button c = sender as Button;

            if (c != null)
            {
                c.MyEvent -= new EventHandler(this.ClickHandler);
            }
        }

        else
        {
            b.Handler1(sender, args);
        }
    }
}

To use the above would be like this:

Subscriber1 sub1 = new Subscriber1();
WeakSubscriber1 weak = new WeakSubscriber1(sub1);
Button button = new Button();
button.Click += weak.ClickHandler();

Now button is a holding a weak reference. Which means when sub1 is made null and the button fires the event, then weak will check if sub1 is null. If yes, it will then check if button is still alive. If both are true, then it will unsubscribe. Now sub1 is no longer subscribing to button.Click so GC can collect it.