【问题标题】:Pattern to unsubscribe events in CustomControls在 CustomControls 中取消订阅事件的模式
【发布时间】:2014-07-28 22:27:25
【问题描述】:

我正在开发一个自定义控件,它在内部订阅 Touch.FrameReported - 一个静态事件。这有可能导致内存泄漏(在某些情况下确实如此)。

这是我目前的解决方案。在 Loaded / Unloaded 事件中订阅 / 取消订阅。但是,我发现并不总是调用 Unloaded 事件。这可能导致内存泄漏。

// Imagine this is a CustomControl, to be consumed by users 
// with no regard for calling Dispose
public class CustomGrid : Grid
{   
    public CustomGrid()
    {
        Loaded += (s, a) =>
                  {
                      Touch.FrameReported -= OnTouchFrameReported;
                      Touch.FrameReported += OnTouchFrameReported;
                  };

        Unloaded += (s, a) =>
                  {
                    // The intention is to unsubscribe on unload, which should pre-date
                    // user intended 'disposal' of the control
                    Touch.FrameReported -= OnTouchFrameReported;
                  };
    }

有解决这个问题的已知模式吗?取消订阅自定义控件“拆卸”中的事件?我已经试过了:

  • 取消订阅已卸载。并不总是被调用。
  • 处置。无法使用,因为用户可能无法确定性地调用 Dispose
  • Weak Events。很好,但是许多实现不适合 WinRT/Silverlight,或者,它们需要显式取消注册,或者,它们仅在事件被调用时才取消注册(Duh!它是一个弱事件)!
  • 终结器。如果有像事件处理程序这样的 GC 根,Finalizer 不会被阻止吗?

【问题讨论】:

  • 你能确定是什么具体情况导致Unloaded事件没有被引发吗?

标签: c# wpf events memory-leaks windows-runtime


【解决方案1】:

tl;dr 如果您使用#1(卸载时取消订阅)和#3(弱事件侦听器)的组合,那么我认为您的控件不应该因任何内存泄漏而出错。你无能为力。

实现IDisposable 并没有真正的帮助,因为没有人愿意在 UI 元素上调用“Dispose”,而且无论如何,在那些没有调用“Unloaded”的情况下,要求“Dispose”只会踢罐头在路上。你是对的,如果有一个静态事件作为其调用列表的一部分保留在你的控件上,则不会调用终结器。

我的理解是,当您的控件从可视树中移除时,应该调用“Unloaded”。因此,如果“卸载”没有被触发,它肯定应该是,那么框架控件中的某个地方存在错误 (which does seem to be a possibility),或者您的用户代码中存在阻止您的控件容器的错误从被卸载。在任何一种情况下,您的控件都不会成为内存泄漏的根源。

使用弱事件处理程序可能是一个很好的故障保护 - 如果对它的唯一引用是弱事件侦听器,它允许您的控件被 GC(因此,这将防止您的“FrameReported”侦听器导致内存泄漏)。我理解您关于实现的观点——实现起来似乎很棘手,但原则上该技术没有任何问题(您可能知道,框架将其用作绑定的事件侦听器)。

【讨论】:

  • 好主意,但我见过的 WeakEvent 实现(例如,在 [WinRT][1] 中看到这个),需要显式注销,或者在事件实际触发时延迟注销。这使得它们在我的上下文中“不弱”。仅当用户触摸触摸屏时才会触发 Touch.FrameReported 事件,如果控件不在屏幕上,则不会发生这种情况。 [1]:codeproject.com/Articles/786606/WeakEventManager-for-WinRT
猜你喜欢
  • 1970-01-01
  • 2018-01-05
  • 2020-11-11
  • 2011-08-14
  • 1970-01-01
  • 2017-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多