WPF WeakReference和GC

时间:2017-01-08 04:11:27

标签: c# garbage-collection weak-references mediator

我在这里看一个调解员原型https://joshsmithonwpf.wordpress.com/2009/04/06/a-mediator-prototype-for-wpf-apps/

作者指出"我的第一个想法是在WeakReference中存储对Action的引用。由于垃圾收集器将丢弃仅由WeakReference对象引用的对象,因此看起来这可以解决问题。不幸的是,它并不那么简单。这种方法的问题是GC会丢弃Action实例,因为它只被WeakReference引用!"

我的问题不是使用"对Action"的引用,为什么"对MethodInfo的引用"做诀窍?我假设也应该收集methodinfo。

提前致谢。

1 个答案:

答案 0 :(得分:2)

  

我的问题不是使用"对Action"的引用,为什么"对MethodInfo的引用"做诀窍?我假设也应该收集methodinfo。

不,实际上并不是MethodInfo vs Action。这是别的,但我必须承认,这篇文章写得不是很好,可能会令人困惑。让我试着解释一下。

活动和GC

我们假设您有一个名为Click的活动。课程是:Button 让我们假设你有另一个类,从Button订阅这样的事件:

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

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

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

现在按钮类如何知道单击按钮时要通知的人?好吧,它保留了对已订阅的所有类的引用。因此,在上面的情况下,它将包含对Subscriber1类的实例的引用。每当点击该按钮时,它将通知Subscriber1的实例。那么如果我们这样做会发生什么:

subcriber1Instance = null;

如果button实例仍然存在,GC将不会收集subscriber1Instance。为什么?因为它是root的:button实例持有对它的引用。这就是为什么我们应该这样做:

subscriber1Instance.UnSubscribe();
subcriber1Instance = null;

实际上Subscriber1应该实现IDisposable并在那里取消订阅,但这个答案的重点不是那个。所以我保持简单,所以我们不会失去焦点。

当我们取消订阅Click实例的button事件时,subscriberIntance不再生根,并且可以由GC清除。

那么文章在做什么?

在那篇文章中,作者试图解决这个问题,即开发人员忘记取消订阅,从而导致内存泄漏问题。请注意,这是一种方式:只有当发布者超过订阅者时,它才会使订阅者保持活跃,而不是相反。因此,如果发布者已准备好使用GC,则订阅者将无法使其保持活动状态。

基本上,作者所说的是订阅者不应该直接订阅该事件,因此不会导致button持有对它的硬引用。相反,订阅者应该从WeakReference派生,并在订阅时将其传递给button。像这样:

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);
        }
    }
}

使用上述内容将是这样的:

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

现在button是一个弱引用。这意味着当sub1为空且button触发事件时,weak将检查sub1是否为空。如果是,它将检查button是否仍然存在。如果两者都是真的,那么它将取消订阅。现在sub1已不再订阅button.Click,因此GC可以收集它。