ReactiveUI 是否会泄漏订阅?

发布于 2024-12-27 12:20:56 字数 274 浏览 1 评论 0原文

我查看了博客中的 ReactiveUi 示例,我想知道 ReactiveUI 是否有某种订阅管理工具,或者这些示例是否忽略了它们可能泄漏订阅的事实?

每当我在 ReactiveUi 中调用导致 IDisposable 的方法时,我是否需要保留该引用并自己跟踪它?这是否意味着我的 ViewModel 需要是一次性的,这似乎很困难,因为我们并不真正知道 WPF 中连接的“视图”何时消失(即,如果我的 ViewModel 反映数据网格中的项目),因此似乎没有合适的方法调用dispose的地方。

I've looked at the examples of ReactiveUi from the blogs and I am left wondering if ReactiveUI has some sort of subscription management facility underneath or are the examples ignoring the fact that they might leak subscriptions?

Any time I call a method in ReactiveUi that results in an IDisposable, do I need to hold onto that reference and track it myself? Also does this mean my ViewModels need to be disposable, this seems difficult since we don't really know when the connected "Views" go away (i.e. if my ViewModel reflects items in a data grid) in WPF so there seems to be no appropriate place to call dispose.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

随风而去 2025-01-03 12:20:56

您还必须记住,Rx 和 ReactiveUI 返回的 IDisposables 与非托管内存无关 - 它只是简单的 .NET 对象,仍然由垃圾收集器引用。

您在 ReactiveObject 的构造函数中进行的大多数订阅都将与宿主对象的生命周期相关联 - 因此,当它超出范围并受到 GC 影响时,所有订阅也会如此,CLR 将检测循环引用并用核武器摧毁一切。

正如 Enigmativity 提到的,一个棘手的问题是当您使用 FromEventPattern 将订阅(也许还有 ViewModel)的生命周期与 WPF 对象的生命周期联系起来时。然而,我认为如果您经常在 ReactiveUI 中使用 FromEventPattern,那么您绝对是 Doing It Wrong™。

RxUI 都是关于 ViewModel 的,而 ViewModel 都是关于命令和属性(以及连接属性如何相互关联)的,因此您可以测试行为 将用户体验与其视觉效果分开。

You also have to remember, that the IDisposables returned by Rx and ReactiveUI aren't associated with unmanaged memory - it's all just simple .NET objects, still ref'ed by the garbage collector.

Most of the Subscriptions you make in the constructors of your ReactiveObjects will be tied to the lifetime of the host object - so when it goes out of scope and is subject to GC, so will all the subscriptions, the CLR will detect the circular reference and just nuke everything.

As Enigmativity mentions, the one tricky bit is when you use FromEventPattern to tie the lifetime of a Subscription (and perhaps, a ViewModel) to the lifetime of a WPF object. However, I'd argue if you're using FromEventPattern often in ReactiveUI, you are most definitely Doing It Wrong™.

RxUI is all about ViewModels, and ViewModels are all about commands and properties (and wiring up how properties are related to each other), so you can test the behavior of user experience separately from its visuals.

倦话 2025-01-03 12:20:56

如果您需要提前取消订阅可观察对象,则只需保存对订阅返回的IDisposable的引用。当 Observables 通过 OnCompletedOnError 消息终止时,它们自然会调用 Dispose

但是,当您有无限可观察订阅(即 FromEventPattern)时,您确实需要保留引用,但这与在关闭表单/视图之前需要删除事件处理程序完全相同。

You only need to hold references to the IDisposable returned by subscriptions if you need to unsubscribe early from an observable. Observables will naturally call Dispose when they terminate by either the OnCompleted or OnError messages.

You do, however, need to keep references when you have an infinite observable subscription (i.e. FromEventPattern), but this is exactly the same as needing to remove event handlers before you close a form/view.

伏妖词 2025-01-03 12:20:56

为了安全起见,从现在开始我将使用这种模式。我可能会
通过扩展方法修改以工作,但原理是合理的。为了确定你
不要泄漏您想要删除的订阅

class Mesh2D{

    public Mesh2D()
    {
        DisposeOnUnload(CreateBindings());
    }

    // Register all disposables for disposal on
    // UIElement.Unload event. This should be
    // moved to an extension method.
    void DisposeOnUnload(IEnumerable<IDisposable> disposables)
    {
        var d = new CompositeDisposable(disposables);
        var d2 = this.UnloadedObserver()
            .Subscribe(e => d.Dispose());
        var d3 = this.Dispatcher.ShutdownStartedObserver()
            .Subscribe(e => d.Dispose());
        d.Add(d2);
        d.Add(d3);
    }

    // Where your bindings are simply yielded and
    // they are removed on Unload magically
    IEnumerable<IDisposable> CreateBindings()
    {
        // When the size changes we need to update all the transforms on 
        // the markers to reposition them. 
        yield return this.SizeChangedObserver()
            .Throttle(TimeSpan.FromMilliseconds(20), RxApp.DeferredScheduler)
            .Subscribe(eventArgs => this.ResetImageSource());

        // If the points change or the viewport changes
        yield return this.WhenAny(t => t.Mesh, t => t.Viewport, (x, t) => x.Value)
            .Throttle(TimeSpan.FromMilliseconds(20), RxApp.DeferredScheduler)
            .Subscribe(t => this.UpdateMeshChanged());
    }
}

注意我正在使用自动生成的 RX FromEventPattern 进行包装
扩展方法,所以我得到 SizeChangedObserver() 和 UnloadedObserver()
免费的,而不是难以记住的 FromEventPattern 格式。

包装代码是这样生成的

public static IObservable<EventPattern<RoutedEventArgs>> UnloadedObserver(this FrameworkElement This){
    return Observable.FromEventPattern<RoutedEventHandler, RoutedEventArgs>(h => This.Unloaded += h, h => This.Unloaded -= h);
}

上面的模式可能用于解除 IDisposable 的绑定
也可以查看模型。

Just to be safe I will be using this pattern from now on. I will probably
modify to work via extension methods but the principle is sound. To be sure you
don't leak subscriptions you want to drop them

class Mesh2D{

    public Mesh2D()
    {
        DisposeOnUnload(CreateBindings());
    }

    // Register all disposables for disposal on
    // UIElement.Unload event. This should be
    // moved to an extension method.
    void DisposeOnUnload(IEnumerable<IDisposable> disposables)
    {
        var d = new CompositeDisposable(disposables);
        var d2 = this.UnloadedObserver()
            .Subscribe(e => d.Dispose());
        var d3 = this.Dispatcher.ShutdownStartedObserver()
            .Subscribe(e => d.Dispose());
        d.Add(d2);
        d.Add(d3);
    }

    // Where your bindings are simply yielded and
    // they are removed on Unload magically
    IEnumerable<IDisposable> CreateBindings()
    {
        // When the size changes we need to update all the transforms on 
        // the markers to reposition them. 
        yield return this.SizeChangedObserver()
            .Throttle(TimeSpan.FromMilliseconds(20), RxApp.DeferredScheduler)
            .Subscribe(eventArgs => this.ResetImageSource());

        // If the points change or the viewport changes
        yield return this.WhenAny(t => t.Mesh, t => t.Viewport, (x, t) => x.Value)
            .Throttle(TimeSpan.FromMilliseconds(20), RxApp.DeferredScheduler)
            .Subscribe(t => this.UpdateMeshChanged());
    }
}

Note I'm wrapping RX FromEventPattern with automatically generated
extension methods so I get SizeChangedObserver() and UnloadedObserver()
for free rather than the hard to remember format of FromEventPattern.

The wrapping code is generated as such

public static IObservable<EventPattern<RoutedEventArgs>> UnloadedObserver(this FrameworkElement This){
    return Observable.FromEventPattern<RoutedEventHandler, RoutedEventArgs>(h => This.Unloaded += h, h => This.Unloaded -= h);
}

The above pattern could probably be used to unbind IDisposable
view models as well.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文