当前位置:首页 > Windows程序 > 正文

WPF 弱事件

2021-05-24 Windows程序

因为在接触WPF的过程中追查INotifyPropertyChanged的通知原理的时候,发现了 PropertyChangedEventManager这个类,它是继承与WeakEventManager,也就是弱事件管理器,另外在学习MVVM的时候,其类库中也有关于弱引用弱事件方面的代码,然后我又非常的不熟悉,今天我打算深入了解下这个方面。

首先得了解下事件,事件在我的另外一篇里面已经详细讲了,这里就不重复了,但是事件其实有可能会导致内存泄露的,下面先看一个例子:

先申明一个Spy类,里面订阅按钮的Click事件:

1 public class Spy { 2 public void MonitorButton(Button button) { 3 button.Click += new RoutedEventHandler(button_Click); 4 } 5 6 private void button_Click(object sender, RoutedEventArgs e) { 7 Button button = sender as Button; 8 MessageBox.Show(string.Format("You have just clicked button {0}", button.Content), 9 "小报告", MessageBoxButton.OK, MessageBoxImage.Information); 10 } 11 12 private ArrayList _weapons = new ArrayList(1024 * 1024 * 100); 13 }

View Code

然后在main里面侦听4个按钮:

1 _strongSpy = new Spy(); 2 _strongSpy.MonitorButton(_btnA); 3 _strongSpy.MonitorButton(_btnB); 4 _strongSpy.MonitorButton(_btnC); 5 _strongSpy.MonitorButton(_btnD);

View Code

然后在第五个按钮的Click事件里面把Spy类对象清理掉:

1 private void KillSpy(object sender, RoutedEventArgs e) { 2 _strongSpy = null; 3 GC.Collect(); 4 GC.WaitForPendingFinalizers(); 5 6 MessageBox.Show("任务完成"); 7 UpdateTitle(); 8 _btnKillSpy.IsEnabled = false; 9 }

View Code

销毁掉Spy类对象后,再去点击按钮,发现事件依然得到响应,为什么呢?就算_strongSpy销毁,但是因为在_strongSpy里面订阅了按钮的click事件,这种订阅是属于强引用的订阅,除非去除订阅关系。貌似这有点不好理解,下面我先谈谈一般我们事件的一个过程:

1)定义事件委托。

 2)申明事件。

 3)触发事件。

4)订阅事件。其实也就是侦听事件。

 针对这个过程我举个2个例子:

  第一个例子是我另外一篇里面的那个热水器的例子,定义事件委托BoiledEventHandler,可以在很多地方都可以,申明事件对象在热水器类里面,事件名为Boiled,然后在另外调用的类里面,订阅这个事件,也就是观察或者侦听这个热水器对象(观察对象),在热水的过程中,超过95度,就触发Boiled事件。事件源指的应该就是热水器这个对象,因为事件是在热水器这个对象里面,侦听者应该就是调用这个热水器对象的类。

  第二个例子是按钮的Click事件,Click事件的委托类型在某个地方定义,这不重要,然后Click事件申明在按钮控件类里面,那个这个按钮类就是Click事件的源,谁来侦听谁就来订阅这个事件,假设form1来侦听,我们一般在按钮上双击,VS自动帮我们做好了这一切,我们直接写事件处理方法即可。

我们再回到_strongSpy销毁了但是按钮事件却仍然得到响应的这个问题上来,到底为什么呢?我下断点,发现_strongSpy确实为null了,,但是点击按钮的事件处理方法依然存在,但是这个方法又是在Spy这个类里面,这岂不矛盾?如果一直点击按钮,内存的占用量会一直增长下去,这是为什么呢?其实原理是这样的:如果通过+=的方式来订阅了事件,那么就隐式创建了一个对这个侦听者的强引用(还有弱引用的概念),任凭怎么GC,实际的侦听对象在事件源没有销毁之前是不会真正的回收的,就跟这个例子一样,真正的源头是button,如果按钮没有销毁,那么_strongSpy是不会真正回收的,特别是当一个类要是侦听多个其他对象的事件的时候,那情况就更糟糕了,因为其他对象如果没有销毁,销毁这个类对象其实是不会被真正回收的。那么如何解决这种问题呢?肯定是要通过-=的方式来去除侦听关系,通过弱引用,弱事件模式来加强GC的回收。WPF提供了一个接口和一个类来实现该模式,我们就先看看代码吧:

温馨提示: 本文由Jm博客推荐,转载请保留链接: https://www.jmwww.net/file/70574.html