MVVM Light:如何取消注册Messenger

43

我喜欢MVVM Light的Messenger和其灵活性,但是当我忘记显式注销接收者时(在Silverlight 4中),就会出现内存泄漏。

原因在这里有解释,不过我认为显式注销接收者是一个好习惯,而不是依赖Messenger使用弱引用。问题是做起来比说起来难。

  • ViewModels很容易:通常您可以完全控制它们的生命周期,当不再需要它们时,只需Cleanup()即可。

  • Views则更加棘手,因为它们通过DataTemplates进行实例化和销毁。例如,您可以将一个绑定到ObservableCollection<MyViewModel>ItemsControl使用MyView作为DataTemplate。 MyView控件由绑定引擎创建/收集,并且您没有好的方法在其上手动调用Cleanup()。

我有一个解决方案,但想知道它是否是一个不错的模式或是否有更好的替代方案。 我的想法是从ViewModel发送特定消息,告诉相关的View(s)进行释放:

public class MyViewModel : ViewModelBase
{
    ...

    public override void Cleanup()
    {
        // unregisters its own messages, so that we risk no leak
        Messenger.Default.Unregister<...>(this);

        // sends a message telling that this ViewModel is being cleaned
        Messenger.Default.Send(new ViewModelDisposingMessage(this));

        base.Cleanup();
    }
}

public class MyView : UserControl, ICleanup
{
    public MyView()
    {
         // registers to messages it actually needs
         Messenger.Default.Register<...>(this, DoSomething);

         // registers to the ViewModelDisposing message
         Messenger.Default.Register<ViewModelDisposingMessage>(this, m =>
             {
                 if (m.SenderViewModel == this.DataContext)
                     this.Cleanup();
             });
    }

    public void Cleanup()
    {
        Messenger.Default.Unregister<...>(this);
        Messenger.Default.Unregister<ViewModelDisposingMessage>(this);
    }
}

所以当您在viewModel上调用Cleanup()时,使用它作为DataContext的所有视图也将执行其本地的Cleanup()。

你认为呢?我有什么明显的遗漏吗?


8
你的Cleanup()行为也有一种模式。实现IDisposable接口,使用一个已知的模式。这并不能解决你的问题,但至少其他开发人员将会知道你使用Dispose()的意图。 - Ray Booysen
1
另外,你的视图为什么需要在 Messenger 中注册任何内容呢?一切都可以通过绑定完成,不是吗? - Ray Booysen
4
另外,我使用ICleanup而不是IDisposable,因为这是MVVM-Light的ViewModelBase使用的。在这里有一个讨论,其中解释了选择背后的原因。 - Francesco De Vittori
1
我倾向于不使用IDisposable。IDisposable意味着不仅仅是简单的注销,它还意味着在调用Dispose方法后,ViewModel将被处理掉,即准备进行垃圾回收。然而,并非总是如此。这就是为什么我考虑从ViewModelBase类中删除IDisposable接口。这并不意味着我反对使用IDisposable,只是默认情况下使用它并不是一个好主意,这就是为什么我包括ICleanup。很想听听大家的评论。干杯! - LBugnion
2
@Arseny:我更喜欢在视图被处理时立即注销它们,否则即使它们应该是“死”的,它们仍可能响应消息。此外,使用弱引用,对象仍可能存活,直到GC收集它(这不在我的控制范围内)。 - Francesco De Vittori
显示剩余10条评论
3个回答

7

ViewModelLocator类帮助保持viewmodels的集中存储。您可以使用此类来帮助管理新版本并清除旧版本。我总是通过locator从view引用我的viewmodel,因此我始终有可以运行以管理这些内容的代码。您也可以尝试一下。

另外,我使用Cleanup方法调用 Messenger.Unregister(this),它会清除该对象在messenger中的所有引用。您需要每次调用.Cleanup(),但这就是生活 :)


你能给一个ViewModelLocator管理版本并进行清理的代码示例吗?我很难理解... :| - sexta13

1

我没有使用过MVVM Light(虽然我听说它很棒),但如果你想要一个使用弱引用的Messenger实现,请查看这里包含的Messenger http://mvvmfoundation.codeplex.com/


3
MVVM Light 使用弱引用。 - AwkwardCoder

-1
MVVM Light Messenger使用WeakAction(弱引用)。因此,您不需要显式取消注册。

6
好的,但实验结果显示还不够。请重新阅读原问题中的所有评论。 - Francesco De Vittori

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接