【发布时间】:2011-07-12 12:51:58
【问题描述】:
我喜欢 MVVM Light 的 Messenger 及其灵活性,但是当我忘记显式取消注册收件人(在 Silverlight 4 中)时,我遇到了内存泄漏。
原因已解释 here,但我对此很好,因为我相信无论如何明确取消注册收件人而不是依赖 Messenger 使用弱引用是一个好习惯。问题是说起来容易做起来难。
ViewModel 很简单:您通常可以完全控制它们的生命周期,并且可以在不再需要它们时
Cleanup()它们。Views 另一方面则比较棘手,因为它们是通过 DataTemplates 实例化和销毁的。例如。您可以将
ItemsControl和MyView视为DataTemplate,绑定到ObservableCollection<MyViewModel>。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()。
你怎么看?我错过了什么明显的东西吗?
【问题讨论】:
-
你的这种 Cleanup() 行为也有一个模式。改为实现 IDisposable 并使用已知模式。这不会解决您的问题,但至少其他开发人员会知道您使用 Dispose() 的意图
-
另外,您的视图需要向 Messenger 注册任何内容的原因是什么。确定一切都会通过绑定完成吗?
-
@Ray:视图注册了一些 viemModel 启动的操作的消息,这些操作无法通过绑定完成,例如。启动动画,设置滚动偏移(没有双向属性)等。这不是太频繁,但它会发生。
-
另外,我使用的是 ICleanup 而不是 IDisposable,因为这就是 MVVM-Light 的 ViewModelBase 使用的。这里有一个讨论,解释了选择背后的原因。
-
@Arseny:我更喜欢在视图被处理后立即取消注册,否则即使它们应该“死”,它们也可能会响应消息。同样对于弱引用,对象仍然可以存活,直到 GC 收集它(这不在我的控制之下)。
标签: c# silverlight mvvm silverlight-4.0 mvvm-light