0

这个问题基于这个问题: 如何处理包含事件的局部变量

有人说:一个事件处理一个实例,实例的引用计数会增加吗?为什么?

4

3 回答 3

2

正如 Jon Skeet 所说,您的相关问题是倒退的。唯一保持watcher活动状态的可能是它自己的内部实现,它可能(或可能不)向负责反馈“滴答声”的较低级别对象注册一个事件。——但这只是猜想。

单击该按钮两次将产生两个单独 watcher的实例,每个实例都有一个事件订阅者PositionChanged(恰好是同一实例上的相同方法)。

重要的是,在方法退出后保持活动状态的不是订阅者- 它是其他东西(我怀疑隐藏在实现中)。当方法退出时,对特定实例的引用被正确地从堆栈中弹出,但是由于另一个引用被持有在CLR 眼中的有效引用计数仍然大于零 ​​- 因此,符合垃圾收集的条件.PositionChangedwatcherGeoCoordinateWatcherwatcherwatcher

因此,它将继续触发该PositionChanged事件。由于事件中没有任何内容阻止观察者继续,我猜你可能有内存泄漏,因为每次按钮单击都会创建并保留一个观察者实例。

您要么只需要存储和使用一个GeoCoordinateWatcher类,要么在每次处理事件时关闭/处置/停止它。


事件和订阅的通常考虑是注意订阅长寿命对象的短寿命对象。

委托持有对类的特定实例中的特定方法的引用(或者如果它是静态方法,则只是类型本身)。订阅事件会导致事件发布者无意中通过订阅的委托持有对订阅者实例的引用。

显然,如果您将静态方法注册为事件处理程序,您将不会获得此引用计数,因为没有实例。

如果订阅者的生命周期很短,而事件发布者的生命周期很长(如果您不取消订阅),则可能会发生内存泄漏。假设订阅者想要获得 GC 的资格,因为它在某处有针对某个事件的活动订阅并且对象仍然存在,所以在从该订阅列表中删除之前,它无法获得资格。

于 2012-06-12T12:57:40.853 回答
0

因为调用GeoCoordinateWatcher.Start你开始一个新任务。即使它在您的代码中声明为local变量,它也会在函数范围退出后继续存在。

您可以考虑这一点,就像您process从函数开始第三部分一样。函数的范围消失了,但过程仍然存在。

在提供的链接中,如果您单击按钮两次,这将导致同一事件处理程序GeoCoordinateWatcher处理2 个不同的实例。因此,将从 2 个不同的实例调用该事件处理程序两次。GeoCoordinateWatcher

于 2012-06-12T12:58:42.233 回答
0

创建委托对象时,它包含MethodTarget属性。Target属性指向将在其上下文中调用方法的对象(Target第一个参数,aka this)。

在某些情况下,对委托对象的引用将处于活动状态,从而防止Target实例被 GC。它通常发生在你有一个基于插件/插件的应用程序,或者其他一些后期绑定的情况,或者你正在使用委托做很多工作并且委托对象存储在某个集合中,或者你有一个静态字段使用委托对象,(因为从不收集静态字段)等。

请记住,垃圾收集不易受到“循环引用”问题的影响;要使一个对象被认为是“有用的”,它必须可以从当前堆栈中访问。

于 2012-06-12T12:59:01.133 回答