何时取消Xamarin自定义渲染器中的事件

时间:2018-09-13 11:19:11

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

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

为了实现某些特殊功能(禁用剪切和复制),我需要在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;
}

问题

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

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

更新

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

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;
    }
}

从用户界面中删除控件后,应该清除这些渲染器,然后触发OnElementChanged方法吗?

1 个答案:

答案 0 :(得分:2)

在此处查看有关自定义渲染器的文章: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时挂接它们。

至于评论中的后续问题(如果未触发第二个以上条件,我们应该退订):我的理解是,这两个对象(因此,渲染器和本机控件)的生存期是相同的,因此这种情况下,无需手动取消订阅事件。如果我错了,请纠正我。