MVVM Light: как отменить регистрацию Messenger

Мне нравится MVVM Light Messenger и его гибкость, однако я испытываю утечку памяти, когда забываю явно отменить регистрацию получателей (в Silverlight 4).

Причина объясняется здесь, но я в порядке, поскольку я считаю хорошей практикой явно отменить регистрацию получателей, а не полагаться на использование Messenger слабых ссылок. Проблема в том, что проще сказать, чем сделать.

  • ViewModels просты: вы обычно имеете полный контроль над своим жизненным циклом и можете просто Cleanup() их, когда они больше не нужны.

  • Представления, с другой стороны, сложнее, поскольку они создаются и уничтожаются с помощью 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);
    }
}

Поэтому, когда вы вызываете Cleanup() на viewModel, все представления, которые используют его в качестве DataContext, будут также выполнять их локальную очистку().

Как вы думаете? Мне что-то не хватает?

Ответ 1

Класс ViewModelLocator помогает хранить централизованное хранилище для ваших моделей viewmodels. Вы можете использовать этот класс для управления новыми версиями и очистки старых. Я всегда ссылаюсь на свою viewmodel из представления через локатор, поэтому у меня всегда есть код, который я могу запустить, чтобы управлять этими вещами. Вы можете попробовать это.

Кроме того, я использую метод Cleanup для вызова Messenger.Unregister(this), который очищает все ссылки от мессенджера для этого объекта. Вы должны каждый раз вызывать .Cleanup(), но это жизнь:)

Ответ 2

Я не использовал MVVM Light (хотя я слышал отличные вещи), но если вы хотите, чтобы реализация Messenger использовала WeakReferences, проверьте, включен ли Messenger http://mvvmfoundation.codeplex.com/.

Ответ 3

MVVM Light Messenger использует WeakAction (WeakReference). Поэтому вам не нужно явно отменить регистрацию.