Время жизни ReactiveUI ObservableForProperty

Мне любопытно узнать время жизни ObservableForProperty, когда я не вызываю Dispose в Observer явно. В этом сценарии меня не волнует слишком долгое получение подписки и т. д.

В традиционном .NET, если у вас есть события, если вы не отписались, это может потенциально привести к утечке памяти из-за того, что время жизни вашего объекта привязано к событию. например, как предложено в http://msdn.microsoft.com/en-us/magazine/cc163316.aspx< /а> :

События также могут быть сильными корневыми ссылками и, как таковые, могут вносить свой вклад в путь сильных ссылок и, таким образом, влиять на время жизни объекта. Обычные события в общеязыковой среде выполнения (CLR) 2.0 являются двунаправленными сильными ссылками между источником события и прослушивателем и, как таковые, могут поддерживать объект (либо источник, либо прослушиватель), который в противном случае уже должен быть мертв.

Просматривая базу кода ReactiveUI при обнаружении объекта INotifyPropertyChanged, я заметил, что вы используете FromEventPattern для подписки на событие INotifyPropertyChange.

Позволяет ли использование ObservableForProperty решить эту проблему сохранения жизни объекта дольше, создавая надежный путь ссылки?

Спасибо, Гленн


person Glenn Watson    schedule 12.03.2014    source источник
comment
Я заметил в другом потоке, что вы упомянули, что ObservableForProperty имеет некоторый контекст с точки зрения конструкторов, просто любопытно, как это работает.   -  person Glenn Watson    schedule 12.03.2014
comment
Механизм, лежащий в основе ObservableForProperty, зависит от объекта, с которым вы его используете, но его ресурсы всегда высвобождаются, когда у Observable больше нет подписчиков.   -  person Ana Betts    schedule 12.03.2014


Ответы (1)


Вы правы, неправильное использование WhenAny / ObservableForProperty может привести к утечке памяти в вашем приложении, если вы не будете осторожны. Рассмотрим следующий код:

public ItemInAListBoxViewModel(MainWindowViewModel mainWindow)
{
    this.window = mainWindow;

    // Reset the "selected" when the user minimizes
    this.WhenAnyValue(x => x.window.IsMinimized)
        .Where(x => x == true)
        .Subscribe(x => this.IsSelected = false);
}

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

Вы избежите подавляющего большинства этих случаев, если будете использовать только WhenAny для своего собственного объекта (т. е. всегда this.WhenAny, никогда someObject.WhenAny).

Особое примечание о свойствах зависимостей

Несмотря ни на что, вы должны удалить любое значение WhenAny, которое проходит через DependencyProperty, иначе произойдет утечка. Потому что Винда.

Черт, что мне теперь делать?

В ReactiveUI была добавлена ​​новая функция для обработки сценария, в котором вы делаете это, хотя и хотите это сделать, под названием "Активация". Дополнительную информацию можно найти по адресу:

Теперь мы можем определить Scope для «вещей, которые должны быть активны только на экране», которые исчезнут сразу после того, как View и его ViewModel будут удалены с экрана (т. е. удалены из визуального дерева в WPF).

public ItemInAListBoxViewModel(MainWindowViewModel mainWindow)
{
    this.window = mainWindow;

    Activator = new ViewModelActivator();

    // This gets called every time the View for this VM gets put on screen
    this.WhenActivated(d => {
        // The 'd' is for "Dispose this when you're Deactivated"
        d(this.WhenAnyValue(x => x.window.IsMinimized)
            .Where(x => x == true)
            .Subscribe(x => this.IsSelected = false));
    });
}

Чтобы это работало, вот что должно быть правдой:

  1. Ваша VieWModel должна реализовать ISupportsActivation (очень просто)
  2. Представление, связанное с вашей ViewModel, также должно вызывать WhenActivated.

Это звучит очень сложно!

Так выглядит, но это совершенно не так. Просто помните две вещи:

  1. Не применяйте WhenAny через объекты, которые останутся навсегда, если только вы также не останетесь навсегда
  2. Если вам нужно, используйте WhenActivated.
person Ana Betts    schedule 12.03.2014
comment
Это мило. Должен попробовать. Однако почему бы не иметь метод IEnumerable<Disposable> Activate() в интерфейсе ISupportsActivation, из которого вы можете вывести все свои подписки для удаления при деактивации. - person bradgonesurfing; 12.03.2014
comment
В целом это хорошая идея, но проблема возникает, когда вы добираетесь до наследования; если вы создаете подкласс и переопределяете, вы можете испортить базовый класс (хотя я думаю, вы могли бы возразить, что вам все равно следует вызывать base.Activate) - person Ana Betts; 12.03.2014
comment
А в С# нет сладкого yield foreach base.Activate();, поэтому вам придется вручную foreach(var v in base.Activate()){yield v;} - person bradgonesurfing; 12.03.2014