Should I always dispose subscriptions of observables?

我的梦境 提交于 2020-01-06 06:01:07

问题


Should I always dispose observables when the ViewModel automatically goes out of scope and no reference to other classes is maintained?

A little example:

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);
    }
}

From my understanding the subscriptions are plain .NET objects. With no reference outside of the ViewModel class. So when my TestViewModel goes out of scope (i.e. the object is never used anymore and replaced with another one) the GarbageCollector should cleanup all the stuff inside the ViewModel, so I have not to manually call Dispose on the returned IDisposables.

Am I correct here?

Edit
The ReactiveList could also hold other .NET objects. This example is not specific to the immutable string type.


回答1:


This is from Kent Boogart (one of the ReactiveUI maintainers) opinion on the matter:

So, hypothetically...if you're using WhenActivated in a view, when do you dispose of the disposable that it returns? You'd have to store it in a local field and make the view disposable. But then who disposes of the view? You'd need platform hooks to know when an appropriate time to dispose it is - not a trivial matter if that view is reused in virtualization scenarios. So there's that.

And what about when you execute a reactive command? Do you store off the disposable you get so you can "clean it up" later? I'm gonna guess not, and for good reason. When the execution completes, all observers are auto-unsubscribed anyway. Generally, subscriptions to pipelines that have a finite lifetime (eg. via a timeout) need not be disposed manually. Disposing of such a subscription is about as useful as disposing of a MemoryStream.

In addition to this, I have found that reactive code in VMs in particular tends to juggle a lot of disposables. Storing all those disposables away and attempting disposal tends to clutter the code and force the VM itself to be disposable, further confusing matters. Perf is another factor to consider, particularly on Android.

So my advice stems from all this. I find that calling out those subscriptions that require disposal by wrapping them in WhenActivated is the most pragmatic approach.




回答2:


To answer questions like these for your specific case, you'll have to use the diagnostic tools to figure out what works in your case.

One test run with the using block and one without:

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())
        {                
        }
    }
}

No manual dispose:

Both subscriptions disposed:

For 1k iterations, the differences are minor and GC is doing its job. The differences are mostly WeakReference types.

Ultimately, as Glenn Watson says, you have to decide on a case by case basis. Observables which use periodic scheduling are a good candidate for disposing manually.

ReactiveUI Guidelines for when to dispose subscriptions




回答3:


You should never rely on the garbage collector to dispose your object cleanly.

Finalizers have a maximum execution time and may be executed in any order, and Dispose will not even be called if the Disposing pattern is properly implemented.

Dispose should clean up managed resources and finalizers should clean up unmanaged resources to avoid memory leaks. The Disposing(bool) pattern will call both on explicit Dispose, and only unmanaged on garbage collection.

Explicit Dispose also allows you to clean up earlier, rather than waiting until the GC collects.

Specifically for subscriptions, event handlers, etc not clearing references can mean that when the GC does collect, references still exist, in which case the object would not be collected at all.

If you know there are no paths to root, you may not strictly have to explicitly dispose, but often in this case someone comes along later and accidentally keeps a reference to one object in the graph and before you know it, the whole graph can never be collected.



来源:https://stackoverflow.com/questions/50829345/should-i-always-dispose-subscriptions-of-observables

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!