铁血规则:事件预订与取消预订

  在编码的时候,我们经常预订某个事件来处理它,但很少取消事件的预订,这种做法可能导致程序在运行时出现一些异常。

      如果你的某个用于处理事件的对象不是在运行期内永久存在的(比如,不是Singleton对象),那么请记住一条规则:在该对象(事件预订者)的生命周期中只要预订了其他对象(事件发布者)的事件,那么在该对象释放时,一定要取消这些事件的预订。否则,在预订者被释放后,发布者仍然保持着预订者的引用,在对应的事件被触发时,发布者仍然会持有预订者的引用(导致内存泄露),并且调用预订者的处理函数,而由于预订者已经被释放,所以可能引发莫名其妙的问题。(这条规则很早就总结出来了,最近却忘记了,以至于浪费了半天的时间来跟踪一个奇怪的现象。以此记录作为前车之鉴,呵呵)

      实践这条规则很简单,一般这样做就可以了:

(1)在预订者的构造函数或初始化函数中预订事件。

(2)在预订者的析构函数或Dispose方法中取消事件预订。

      比如:

    public class Publisher
    {
        
public event CbGeneric SomeEvent;
    }

    
public class Subscriber :IDisposable
    {
        
private Publisher publisher;

        
public Subscriber(Publisher _publisher)
        {
            
this.publisher = _publisher ;
            
//预订事件
            this.publisher.SomeEvent += new CbGeneric(publisher_SomeEvent);
        }

        
void publisher_SomeEvent()
        {
            
//处理事件
        }

        
public void Dispose()
        {
            
//取消预订
            this.publisher.SomeEvent -= new CbGeneric(publisher_SomeEvent);
        }
    }

      特别是当预订者是自定义的windows控件时(从Control类继承),我们可以在其自身的Disposed事件中,来取消对发布者的事件预订。当包含该控件对象的宿主Form被关闭时,控件对象也会被释放,这可能是一个很隐蔽的问题,以至于我们忘了在控件被释放时取消必须的事件预订。

      我们也许想到,如果发布者与预订者的生命周期是完全相同的,是不是就不需要取消预订了?大多数情况下是可以的,但是你要保证你的发布者对象在被释放后,是否还被其他的对象持有引用,这样也可能会导致内存泄露以及其他问题。所以,我们建议,既然预订了事件,就请在预订者被释放时,取消这些预订。

 

 

 

 

posted on 2011-04-07 17:19 zhuweisky 阅读(1622) 评论(5) 编辑 收藏

评论

#1楼 2011-04-07 17:25 风云      

推荐,否则就会内存泄露!  回复 引用 查看   

#2楼 2011-04-07 17:29 yyww      

@风云
嗯,是的。 也可以使用WeakEvent来实现
 回复 引用 查看   

#3楼 2011-04-07 17:37 风云      

08年的时候,也总结过这个问题:委托 - 事件 - 内存泄漏 - 弱引用 让人欢喜让人"忧"  回复 引用 查看   

#4楼 2011-04-07 17:53 徐少侠      

的确很隐蔽哦
 回复 引用 查看   

#5楼 2011-04-07 21:34 思无邪      

事件造成内存泄露应该是使用事件方式不对吧,只有GC在对象图不能发现的对象才会被回收。如果真的存在这种方式的内存泄露,即使实现了IDisposable
仍然会泄露
 回复 引用 查看   

导航

公告

             

人的灵魂要强,

接受人的脆弱。

昵称:zhuweisky
园龄:6年7个月
荣誉:推荐博客
粉丝:210
关注:10

统计

搜索

 
 

随笔分类(267)

随笔档案(283)

积分与排名

最新评论

阅读排行榜

推荐排行榜