人気ブログランキング | 話題のタグを見る

INotifyPropertyChangedとメモリリークの問題


BindPropertyを作っていて初めて知ったのですが、ViewModelがINotifyPropertyChangedを継承していないと、どうやら参照が残ってViewModelが破棄されない問題があるそうです。


検証してみましたが、確かに残ります。えー、メンドイ……。
メインウィンドウ1つだけならいいんでしょうけど、サブウィンドウを使ったりする場合はちょっと嫌ですね。
自分で調べた限りでは対策方法は2つあります。
どちらかお好みの方法を選んで使っていくしかないようです。
結局、プロパティ変更通知はBindPropertyのような邪道なやり方はせず、王道路線で行くのが一番いいのかもしれません……。

【INotifyPropertyChangedを空実装する】
使わないけど実装だけしておく。
それだけだとPropertyChangedが使われていませんってコンパイラに警告されまくるので、PropertyChanged?.Invoke(null,null)みたいに使っているフリをする必要がある。

【Window_Closeイベントでバインドを解除する】
View側でバインドを解除してあげる。
BindPropertyのサンプルアプリケーションではこの方法を取っていますが、これもこれでWindowやPageが多いと大変です。
基底クラスにする手もありますが……。

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}

private void Window_Closed(object sender, EventArgs e)
{
// メモリリーク対策
// ViewModelがINotifyPropertyChangedを継承していなければすべてのコントロールのバインドを解除する
if (!(DataContext is System.ComponentModel.INotifyPropertyChanged))
WalkInChildren(this, (o) => BindingOperations.ClearAllBindings(o));
}

// すべてのコントロールを列挙
private void WalkInChildren(DependencyObject o, Action<DependencyObject> act)
{
foreach (var child in LogicalTreeHelper.GetChildren(o))
{
if (child is DependencyObject)
{
WalkInChildren(child as DependencyObject, act);
}
}
act(o);
}
}

by mikaka-flyff | 2018-09-01 18:54