我喜欢MVVM Light的Messenger及其灵活性,但是当我忘记明确取消注册收件人时(在Silverlight 4中),我遇到了内存泄漏。
原因解释here,但我很好,因为我认为明确取消注册收件人是一个好习惯,而不是依赖于Messenger使用弱引用。问题是说起来容易做起来难。
ViewModels 非常简单:您通常可以完全控制其生命周期,只有Cleanup()
不再需要它们。
视图比较棘手,因为它们是通过DataTemplates实例化和销毁的。对于前者您可以将ItemsControl
与MyView
视为DataTemplate,绑定到ObservableCollection<MyViewModel>
。 MyView
控件由绑定引擎创建/收集,您无法手动调用它们上的Cleanup()。
我有一个解决方案,但想知道它是否是一个体面的模式或有更好的选择。 我们的想法是从ViewModel发送一条特定的消息来告诉相关的View处置:
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()。
你怎么看?我错过了一些明显的东西吗?答案 0 :(得分:6)
ViewModelLocator类有助于为视图模型保留集中存储。您可以使用此类来帮助管理新版本并清理旧版本。我总是通过定位器从视图中引用我的viewmodel,所以我总是有可以运行的代码来管理这些东西。你可以试试。
同样,我使用Cleanup方法调用Messenger.Unregister(this)
,它清除来自该对象的信使的所有引用。你必须每次都调用.Cleanup(),但这就是生活:)
答案 1 :(得分:1)
我没有使用MVVM Light(虽然我听过很棒的东西)但是如果你想要一个使用WeakReferences的Messenger实现,请查看这里包含的Messenger http://mvvmfoundation.codeplex.com/。
答案 2 :(得分:0)
MVVM Light Messenger正在使用WeakAction(WeakReference)。因此,您无需显式取消注册。