Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I always dispose subscriptions of observables?

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.

like image 769
senz Avatar asked Sep 21 '25 11:09

senz


2 Answers

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.

like image 94
Glenn Watson Avatar answered Sep 23 '25 00:09

Glenn Watson


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:

No disposal

Both subscriptions disposed:

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

like image 45
Asti Avatar answered Sep 23 '25 02:09

Asti