【问题标题】:When to unhook events in Xamarin Custom Renderers何时取消挂钩 Xamarin 自定义渲染器中的事件
【发布时间】:2018-09-13 11:19:10
【问题描述】:

众所周知,在代码中连接事件处理时,我们冒着将对象留在内存中的风险,从而造成内存泄漏。

为了实现一些特殊功能(禁用剪切和复制),我需要在 UWP 上实现自定义渲染器。虽然禁用剪切和复制与我必须连接事件处理程序才能实现这一点的问题并不严格相关。

protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
    base.OnElementChanged(e);

    if (this.Control == null) { return; }

    this.Control.CopyingToClipboard += Control_CopyingToClipboard;
    this.Control.CuttingToClipboard += Control_CuttingToClipboard;
}

private void Control_CuttingToClipboard(TextBox sender, 
                                        TextControlCuttingToClipboardEventArgs args)
{
    args.Handled = true;
}

private void Control_CopyingToClipboard(TextBox sender, 
                                        TextControlCopyingToClipboardEventArgs args)
{
    args.Handled = true;
}

问题

解开这些事件处理程序以防止任何形式的泄漏的正确位置是什么?

我注意到UWP 平台命名空间中有一个IDisposable 实现而不是VisualElementRenderer&lt;TElement, TNativeElement&gt;,但是我无法可靠地证明它被调用了。

更新

根据 Michał Żołnieruk 的建议,我在 OldElement 不为空的检查中添加了 unhooking,但是我从未看到任何证据证明这会被调用。

protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
    base.OnElementChanged(e);

    if (this.Control == null) { return; }

    if (e.OldElement != null)
    {
        System.Debug.WriteLine("I NEVER SEE THIS");

        this.Control.CopyingToClipboard -= Control_CopyingToClipboard;
        this.Control.CuttingToClipboard -= Control_CuttingToClipboard;
    }

    if (e.NewElement != null)
    {
        this.Control.CopyingToClipboard += Control_CopyingToClipboard;
        this.Control.CuttingToClipboard += Control_CuttingToClipboard;
    }
}

当从 UI 中移除控件时,是否应该清理这些渲染器并因此触发 OnElementChanged 方法?

【问题讨论】:

    标签: c# xamarin memory-leaks xamarin.forms event-handling


    【解决方案1】:

    在此处查看有关自定义渲染器的文章:Implementing a View 它包含自定义渲染器的 OnElementChanged 方法的模板:

    protected override void OnElementChanged (ElementChangedEventArgs<NativeListView> e)
    {
      base.OnElementChanged (e);
    
      if (Control == null) {
        // Instantiate the native control and assign it to the Control property with
        // the SetNativeControl method
      }
    
      if (e.OldElement != null) {
        // Unsubscribe from event handlers and cleanup any resources
      }
    
      if (e.NewElement != null) {
        // Configure the control and subscribe to event handlers
      }
    }
    

    所以你应该在 OldElement 不为 null 时取消挂钩事件,并在出现 NewElement 时挂钩它们。

    至于 cmets 中的后续问题(如果没有触发上述第二个,我们是否应该取消订阅):我的理解是这两个对象的生命周期(因此渲染器和本机控件)是相同的,并且在这样的如果不需要手动取消订阅事件。如果我错了,请纠正我。

    【讨论】:

    • 谢谢。我不完全确定为什么我没有在我的问题中包含详细信息,但是当我在我的渲染器中实现 e.OldElement 检查并在其中插入一个断点时,我从未能够命中该断点。这让我觉得我可能正在查看内存泄漏。
    • 不客气,我添加了关于它的附加评论。
    猜你喜欢
    • 1970-01-01
    • 2020-04-05
    • 2022-01-02
    • 1970-01-01
    • 2021-12-06
    • 1970-01-01
    • 2020-03-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多