5

Silverlight コントロールでいくつかのブレンド動作とトリガーを使用しています。コントロールが使用されなくなったとき (つまり、ビジュアル ツリーから削除されたとき) に、動作またはトリガーに対して OnDetaching() が自動的にデタッチまたは確実に呼び出されるようにするメカニズムがあるかどうか疑問に思っています。

私の問題は、動作の 1 つが原因で、コントロールにマネージ メモリ リークがあることです。この動作は、OnAttached() オーバーライドで存続期間の長いオブジェクトのイベントにサブスクライブし、OnDetaching() オーバーライドでそのイベントからサブスクライブを解除して、ガベージ コレクションの候補にできるようにする必要があります。ただし、ビジュアルツリーからコントロールを削除すると、OnDetaching() が呼び出されることはないようです...それを実現できる唯一の方法は、コントロールを削除する前に問題のある動作を明示的にデタッチすることであり、適切にガベージコレクションされます.

現時点で私の唯一の解決策は、ガベージ コレクションの問題を引き起こす既知の動作を通過して切り離すことができるコントロールのコード ビハインドにパブリック メソッドを作成することでした。パネルからコントロールを削除する前にこれを呼び出すかどうかは、クライアント コード次第です。私はこのアプローチがあまり好きではないので、見落としている自動化された方法またはより良い提案を探しています。

public void DetachBehaviors()
{
     foreach (var behavior in Interaction.GetBehaviors(this.LayoutRoot))
     {
          behavior.Detach();
     }

     //continue detaching all known problematic behaviors on the control....
}
4

2 に答える 2

3

この場合に本当に必要なのは、自動的にデタッチする方法ではなく、長期間有効なオブジェクトによって保持されている参照が、動作 (したがって、オブジェクトが参照する他のすべてのもの) がガベージ コレクションされないようにすることです。

これは、Mediator パターンを実装することで達成されます。概念は、長期間有効なオブジェクトに への参照を持つデリゲートを与えるのではBehaviourなく、仲介者として Mediator クラスを作成するというものです。メディエーターは長寿命オブジェクト イベントにアタッチし、動作へのWeakReferenceを保持します。存続期間の長いオブジェクトがイベントを発生させると、メディエーターWeakReferenceはまだ生きていることを確認し、生きている場合はそのオブジェクトのメソッドを呼び出してイベントを渡します。イベントが発生したときにメディエーターが がWeakReference生きていないことを発見した場合、メディエーターはそのイベント ハンドラーを長期間存続するオブジェクトから切り離します。

したがって、動作を止めるものは何もなく、ガベージ コレクションの対象となる他のすべてのものは、寿命の長いオブジェクトに関連付けられた無効な参照を持つ非常に小さなメディエーター インスタンスだけが残ります。これらのメディエーターは小さいため、実際の問題を表すものではなく、次にイベントが発生したときにそれらのメディエーターでさえ消えてしまいます。

幸いなことに、このようなものを自分で構築する必要はありません。他の人はすでにそれを行っています。と呼ばれWeakEventListenerます。このブログ: 「弱い」貢献を強調する。機能強化により、WeakEventListener を使用したメモリ リークの防止がさらに簡単になりました。件名に関する優れたリンクのセットがあります。

于 2010-04-23T07:16:01.357 に答える
3

Joost van Schaikは、メモリ リークの問題を回避しながら、添付されたビヘイビアから参照をクリーンアップする別の方法を提供しています。これは、AssociatedObject の Loaded イベントと Unloaded イベントのデリゲートを使用してクリーンアップ作業を行うことに依存します。

彼はまた、添付された動作のスタブを生成するためのコード スニペットも提供しています。

于 2012-02-28T11:09:05.480 に答える