且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

为Silverlight行为自动调用OnDetaching()

更新时间:2023-10-23 12:53:46

在这种情况下真正需要的不是自动分离的方法,而是确保长寿命对象所持有的引用不会保持垃圾收集的行为(并因此引用其他所有内容)。



这是通过实施Mediator模式实现的。这个概念是,你不会给长寿命对象一个委托,引用你的 Behavior ,而是创建一个Mediator类作为中介。介体附加到长寿命对象事件并保存 WeakReference 的行为。当长寿命对象触发事件时,中介检查 WeakReference 是否仍然存在,如果是,则调用它的方法来传递事件。如果事件发生时介体发现 WeakReference 不再活动,它会将其事件处理程序从长期存在的对象中分离出来。


$ b $因此,没有什么能够阻止垃圾收集所涉及的行为和其他一切,剩下的只是一个非常小的中介实例,而死参引依然附加在长寿命对象上。由于这些介体很小,因此它们并不代表真正的问题,即使这些介质在下次事件触发时也会消失。

幸运的是,您不必构建这些东西其他人已经做到了。它被称为 WeakEventListener 。此博客:突出弱贡献;增强功能可以更轻松地防止WeakEventListener发生内存泄漏!在这个主题上有一组优秀的链接。


I am using several Blend behaviors and triggers on a silverlight control. I am wondering if there is any mechanism for automatically detaching or ensuring that OnDetaching() is called for a behavior or trigger when the control is no longer being used (i.e. removed from the visual tree).

My problem is that there is a managed memory leak with the control because of one of the behaviors. The behavior subscribes to an event on some long-lived object in the OnAttached() override and should be unsubscribing from that event in the OnDetaching() override so that it can become a candidate for garbage collection. However, OnDetaching() never seems to be getting called when I remove the control from the visual tree... the only way I can get it to happen is by explicitly detaching the problematic behaviors BEFORE removing the control and then it is properly garbage collected.

Right now my only solution was to create a public method in the code-behind for the control that can go through and detach any known behaviors that would cause garbage collection problems. It would be up to the client code to know to call this before removing the control from the panel. I don't really like this approach, so I am looking for some automatic way of doing this that I am overlooking or a better suggestion.

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

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

What you really need in this case is not some way to automatically detach but to ensure that the reference held by the long lived object does not keep the behaviour (and therefore everything else it has a reference to) from being garbage collected.

This is acheived by implementing a Mediator pattern. The concept is that you don't give the long-lived object a delegate with a reference to your Behaviour, instead you create a Mediator class as a go-between. The mediator attaches to the long-lived objects event and holds a WeakReference to the behaviour. When the long-lived object fires the event the mediator checks that the WeakReference is still alive, if so calls a method on it to pass on the event. If when the event occurs the mediator finds that the WeakReference is no longer alive it detaches its event handler from the long lived object.

Hence there is nothing stopping the behaviour and everything else involved from being garbage collected all thats left is a very small mediator instance with a dead reference still attached to the long-lived object. Since these mediators are tiny they don't represent a real problem and even those will disappear the next time the event fires.

Fortunately you don't have to build this stuff yourself others have already done it. It is called the WeakEventListener. This blog: Highlighting a "weak" contribution; Enhancements make preventing memory leaks with WeakEventListener even easier! has an excellent set of links on the subject.