我应该总是处置可观察的订阅吗?

时间:2018-06-13 04:59:45

标签: c# system.reactive reactiveui

ViewModel自动超出范围并且不保留对其他类的引用时,我是否应该始终处置observable?

一个小例子:

public class TestViewModel : ReactiveObject
{
    public TestViewModel()
    {
        MyList = new ReactiveList<string>();
        MyList.ChangeTrackingEnabled = true;

        //Do I have to dispose/unsubscribe this?
        MyList.ItemChanged.Subscribe(_ => ...);

        //Or this?
        this.WhenAnyValue(x => x.MyList).Subscribe(_ => ...);
    }

    ReactiveList<string> _myList;
    public ReactiveList<string> MyList
    {
        get => _myList;
        set => this.RaiseAndSetIfChanged(ref _myList, value);
    }
}

根据我的理解,订阅是纯.NET对象。没有ViewModel类之外的引用。所以当我的TestViewModel超出范围时(即object永远不再使用并被另一个替换)GarbageCollector应该清理ViewModel中的所有内容,所以我在返回的Dispose上手动调用IDisposables

我在这里纠正吗?

修改
ReactiveList也可以容纳其他.NET对象。此示例并非特定于不可变字符串类型。

3 个答案:

答案 0 :(得分:2)

这是来自Kent Boogart(ReactiveUI维护者之一)对此事的看法:

所以,假设......如果您在视图中使用WhenActivated,何时处理它返回的一次性用品?您必须将其存储在本地字段中并使视图处于一次性状态。但那么谁处置了这个观点呢?您需要平台挂钩来了解何时处理它的适当时间 - 如果该视图在虚拟化方案中重用,则不是一件小事。所以就是这样。

当你执行反应命令时怎么办?你是否存放了你得到的一次性物品,以便你可以清理它&#34;后来?我猜不会,并且有充分的理由。执行完成后,无论如何都会自动取消订阅所有观察者。通常,不需要手动处理对具有有限生命周期的管道的订阅(例如,通过超时)。处置此类订阅与处置MemoryStream一样有用。

除此之外,我发现VM中的反应代码特别容易兼顾大量的一次性用品。将所有那些一次性物品存放起来并试图处理往往会使代码混乱并迫使VM本身变成一次性的,这进一步使事情变得混乱。 Perf是另一个需要考虑的因素,特别是在Android上。

所以我的建议源于这一切。我发现通过将它们包装在WhenActivated中来调出那些需要处理的订阅是最实用的方法。

答案 1 :(得分:1)

要针对您的具体案例回答类似这些问题,您必须使用诊断工具来确定适用于您案例的内容。

使用y2块进行一次测试,一次不使用:

using

无人工处理:

No disposal

两个订阅都已处理:

Both subscriptions disposed

对于1k次迭代,差异很小,GC正在完成其工作。 差异主要是class Program { static void Main(string[] args) { //warmup types var vm = new TestViewModel(); Console.ReadLine(); //Snapshot #1 for (int i = 0; i < 1000; i++) Model(); GC.Collect(); Console.ReadLine(); //Snapshot #2 } private static void Model() { using (var vm = new TestViewModel()) { } } } 类型。

最终,正如Glenn Watson所说,你必须根据具体情况来决定。使用定期调度的可观察量是手动处理的良好候选者。

ReactiveUI Guidelines for when to dispose subscriptions

答案 2 :(得分:0)

你永远不应该依赖垃圾收集器来干净地处理你的对象。

终结器具有最长执行时间,可以按任何顺序执行,如果正确实施了处置模式,则甚至不会调用Dispose

Dispose应该清理托管资源,终结器应该清理非托管资源以避免内存泄漏。 Disposing(bool)模式将在显式Dispose上调用,并且仅对垃圾回收进行非托管。

显式Dispose也允许您提前清理,而不是等到GC收集。

特别是对于订阅,事件处理程序等不清除引用可能意味着当GC收集时,引用仍然存在,在这种情况下,根本不会收集对象。

如果你知道没有root的路径,你可能不一定要明确地处理,但通常在这种情况下有人出现以后意外地保持对图中一个对象的引用,在你知道它之前,整个无法收集图表。