4

私は、C# と WPF を使用して構築されたアプリケーションに取り組んでいます。これは (ひどく) MVVM を実装しています。仕事の内訳は次のようなものです。

  • 意見

    • プレゼンテーション、ビットマップ、色など
    • アニメーション(使用する場合)
    • ViewModel にデータ バインドすることで、ViewModel と通信します。
    • コマンドを発生させることにより、ViewModel と通信します。
  • ビューモデル

    • ビューによって呼び出されるコマンドを公開します
    • データ処理機能をモデルに委任します
    • UI の動作を定義します
    • ビューへの直接的な (名前付きの) 依存関係はありません
    • モデル内のメソッドを呼び出してモデルと通信します
    • モデルによって公開されたイベントをサブスクライブすることにより、モデルの変更が通知される場合があります
  • モデル

    • ディスクの永続性、データ分析など
    • ほかのすべて
    • ViewModel に依存しない

残念ながら、これにより循環参照が発生しました。ビューは、イベントを発生させてコマンドを起動するためにビューモデルへの参照が必要であり、ビューモデルはビューステートを更新するためにビューへの参照を必要とするためです (通常、この参照は WPF のビューモデルによるものDataContextです)。作業を委任するためのモデルへの参照、およびモデルは多くの場合、状態の外部変化をビューモデルに通知する必要があります。

したがって、ビューモデルが真ん中にあるという 2 つの循環参照の問題があります。その結果、WPF エンティティが作成され、モデル内の一部のデータに関連付けられており、これらのエンティティは (プログラムが終了するまで) クリーンアップされないため、このアプリケーションではメモリ消費の問題が発生しています。

これはどのように扱われるべきですか?

所有権グラフを定義する必要があるようです。これらのコンポーネントの 1 つまたは複数が、関連性がなくなったときにイベント ハンドラーを切断する責任を負い、物事を GC できるようにします。

4

3 に答える 3

1

わかりました、それらすべてを直接一緒に使用したと思いました

キャッシュされたビューがない場合、メモリ使用量が多いのはなぜですか?

MVVM アプリケーションでメモリの問題が発生することはありません (よく構築されていないものでも)。どのような静的項目がありますか?

このコードを使用して、wpf アプリケーションのバインディングの現在の状況を調査したことがありますか?

private static IList<BindingInfo> getReflectPropertyDescriptorInfo()
        {
            var results = new List<BindingInfo>();

            var reflectTypeDescriptionProvider = typeof(PropertyDescriptor).Module.GetType("System.ComponentModel.ReflectTypeDescriptionProvider");
            var propertyCacheField = reflectTypeDescriptionProvider.GetField("_propertyCache",
                BindingFlags.Static | BindingFlags.NonPublic);
            if (propertyCacheField == null)
                throw new NullReferenceException("`ReflectTypeDescriptionProvider._propertyCache` not found");

            var propertyCacheItems = propertyCacheField.GetValue(null) as Hashtable;
            if (propertyCacheItems == null)
                return results;

            var valueChangedHandlersField = typeof(PropertyDescriptor).GetField("valueChangedHandlers",
                BindingFlags.Instance | BindingFlags.NonPublic);

            if (valueChangedHandlersField == null)
                return results;

            foreach (DictionaryEntry entry in propertyCacheItems)
            {
                var properties = entry.Value as PropertyDescriptor[];
                if (properties == null)
                    continue;

                foreach (var property in properties)
                {
                    var valueChangedHandlers = valueChangedHandlersField.GetValue(property) as Hashtable;
                    if (valueChangedHandlers != null && valueChangedHandlers.Count != 0)
                        results.Add(new BindingInfo
                            {
                                TypeName = entry.Key.ToString(),
                                PropertyName = property.Name,
                                HandlerCount = valueChangedHandlers.Count
                            });
                }
            }

            return results;
        }

このコードを使用すると、メモリ内にあるバインドを確認できます。

于 2013-09-12T07:43:55.010 に答える
0

" ビューモデルはビューステートを更新するためにビューへの参照を必要とします (通常、この参照は WPF の DataContext であるビューモデルによるものです), " - この時点では依存関係はありません。ビューに関するものなので、ビューのビューモデル内の明確な依存性ではありません。

ViewModel を DataContext に割り当てる必要があるときに依存関係があり、View が ViewModel に依存している (ViewModel の代わりに IViewModel を使用できるため、その必要はありません)

ViewModel の最初のアプローチでも、実際には View を持っていませんが、View によって実装される IView コントラクトがあります。

class ContentViewModel{


 IView view;

 public ContentViewModel(IContentAView view)
       {
            View = view;
            View.ViewModel = this;

コンテナーを Unity として使用し、View Model または View が毎回インスタンス化されているが、参照 (おそらくイベント ハンドラー) のために古いインスタンスが GC ではない場合、メモリ リークが発生します。Microsoft WinDbg の RedGate メモリ プロファイラなどのツールを使用して、メモリをプロファイリングし、新しいインスタンスがいつ作成されたかを調べ、古いインスタンスへの参照を見つける必要があります。

于 2013-09-12T08:32:50.090 に答える