WeakPropertyChangedListener用弱引用事件订阅防止内存泄露

GC对象回收机制

一个对象会不会被回收,主要看垃圾回收器引用遍历的时候能不能访问到这个引用,如果访问不到,则将被回收,如果能够访问到,则不能回收。也就是说GC回收前GC会确定对象没有任何引用。GC对“无用”对象的识别机制就是判断对象是否被“根(Root)”所引用。在这里,“根”是对一组当前正被使用,或者以后可能被使用的对象的统称,大体包括这样的对象:类型的静态字段或当前的方法参数和局部变量、CPU寄存器等。

如何确定对象已经被释放而没有内存泄露

使用ANTS Memory Profiler(Redgate)这个工具,把程序运行起来,打开一个Form,然后拍一张快照;关闭那个Form,再拍一张快照,两张快照比较一下,看看Form释放了吗?如果没有释放,看看它被谁引用而导致GC不释放它。ANTS Memory Profiler的强大之处就是可以让你可以很清楚地看到这个对象正在被那些其他的对象引用着。Redgate推荐你强烈关注没有被释放的对象是否被类型为EventHandler<T>的事件引用,这个是很常见的内存泄露处。上面的例子的解决办法也很简单,就是在Form关闭的事件中加一个EventHandler -= ...来释放事件监听就行了。EventHandler本质是Delegate,  通过它把事件Listener成为了引用者。EventHandler+=...这句话把两个对象联系了起来,通过它把事件Listener成为了引用者,也就是说一个对象被另一个对象强引用,当然不能被GC释放了。另外一个解决方法就是弱引用 - WeakReference。当然和强引用相比会有稍许性能损失。

弱引用 - WeakReference

以上说的都是强引用;GC就是通过检查强引用来决定一个对象是否是可以回收的。还有一种引用称作弱引用(WeakReference),这种引用不影响GC回收,这就是它的用处所在。例如,你有一个很大的Static变量作缓存的,Static变量是不会被回收的。这时候我们可以创建一个这个大对象的弱引用,这样在内存不够时GC可以回收,不影响内存使用,而在没有被GC回收前我们还可以再次利用该对象。

IPropertyChanged与内存泄露

IPropertyChanged这个接口很好,通常用来通知client(例如,绑定的界面),例如,一个实现了IPropertyChanged接 口的Person类,当其属性IsBusy的值变化的时候就自动发出通知给Listener,如一个视图绑定到这个视图模型Person类,其中的一个 ProgressBar控件绑定到这个IsBusy属性,然后用一个Converter把bool的值转换为ProgressBar的状态,这就通过视图 和模型的绑定和事件侦听实现了自动更新通知。但可悲的是,整个过程的事件侦听有内存泄露,具体原因见上面的GC原理分析。解决办法就是自己实现一个没有内存泄露的WeakPropertyChangedListener.

 

WeakPropertyChangedListener用弱引用事件订阅防止内存泄露

WeakPropertyChangedListener: 核心实现思想是使用WeakReference弱引用,当事件侦听的父对象回收的时候回收内存。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace ConsoleApplication1
{
   public class WeakPropertyChangedListener
        {
            #region Private Fields
            private INotifyPropertyChanged _source;
            public WeakReference _listener;
            #endregion
            #region Ctor
            public WeakPropertyChangedListener(INotifyPropertyChanged source, PropertyChangedEventHandler listener)
            {
                this._source = source;
                this._source.PropertyChanged += this.OnPropertyChanged;
                this._listener = new WeakReference(listener);
            }
            #endregion
            #region Public Methods
            public static WeakPropertyChangedListener Create(INotifyPropertyChanged source, PropertyChangedEventHandler listener)
            {
                return new WeakPropertyChangedListener(source, listener);
            }
            public void Disconnect()
            {
                if (_source != null)
                {
                    _source.PropertyChanged -= this.OnPropertyChanged;
                    _source = null;
                    _listener = null;
                }
            }
            #endregion
            #region Private Methods
            private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                if (_listener != null)
                {
                    var handler = _listener.Target as PropertyChangedEventHandler;
                    if (handler != null)
                    {
                        handler(sender, e);
                    }
                    else
                    {
                        this.Disconnect();
                    }
                }
            }
            #endregion
        }
}

INotifyPropertyChanged实现类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace ConsoleApplication1
{
    public class TestViewModel : INotifyPropertyChanged
    {
        private int _Value;
        public int Value
        {
            get
            {
                return _Value;
            }
            set
            {
                if (value != _Value)
                {
                    _Value = value;
                    NotifyPropertyChanged("Value");
                }
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        } 
    }
}

测试Unit testing

[TestMethod()]
        public void CreateTest()
        {
            TestViewModel source = new TestViewModel();
            PropertyChangedEventHandler listener = OnProperChangedHandler;
            WeakPropertyChangedListener actual = WeakPropertyChangedListener.Create(source, listener);
            bool fired = false;
            source.PropertyChanged += (s, a) =>
            {
                if (a.PropertyName == "Value")
                    fired = true;
            };
            source.Value = 5;
            Assert.IsTrue(fired);
            listener = null;//释放对象
            GC.Collect(); //垃圾回收
            source.Value = 6;
            Assert.IsNull(actual._listener); //暂时设_listen为public便于测试
        }
        public void OnProperChangedHandler(object sender, PropertyChangedEventArgs args)
        {
        }

本文示例源码下载

原文地址:https://www.cnblogs.com/Mainz/p/2090199.html