Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a weak subscription to an IObservable

Tags:

What I want to do is ensure that if the only reference to my observer is the observable, it get's garbage collected and stops receiving messages.

Say I have a control with a list box on it called Messages and this code behind:

//Short lived display of messages (only while the user's viewing incoming messages) public partial class MessageDisplay : UserControl {     public MessageDisplay()     {         InitializeComponent();         MySource.IncomingMessages.Subscribe(m => Messages.Items.Add(m));     } } 

Which is connecting to this source:

//Long lived location for message store static class MySource {     public readonly static IObservable<string> IncomingMessages = new ReplaySubject<string>; } 

What I don't want is to have the Message Display being kept in memory long after it's no longer visible. Ideally I'd like a little extension so I can write:

MySource.IncomingMessages.ToWeakObservable().Subscribe(m => Messages.Items.Add(m)); 

I also don't want to rely on the fact that MessageDisplay is a user control as I will later want to go for an MVVM setup with MessageDisplayViewModel which won't be a user control.

like image 614
ForbesLindesay Avatar asked Sep 06 '11 15:09

ForbesLindesay


2 Answers

You can subscribe a proxy observer to the observable that holds a weak reference to the actual observer and disposes the subscription when the actual observer is no longer alive:

static IDisposable WeakSubscribe<T>(     this IObservable<T> observable, IObserver<T> observer) {     return new WeakSubscription<T>(observable, observer); }  class WeakSubscription<T> : IDisposable, IObserver<T> {     private readonly WeakReference reference;     private readonly IDisposable subscription;     private bool disposed;      public WeakSubscription(IObservable<T> observable, IObserver<T> observer)     {         this.reference = new WeakReference(observer);         this.subscription = observable.Subscribe(this);     }      void IObserver<T>.OnCompleted()     {         var observer = (IObserver<T>)this.reference.Target;         if (observer != null) observer.OnCompleted();         else this.Dispose();     }      void IObserver<T>.OnError(Exception error)     {         var observer = (IObserver<T>)this.reference.Target;         if (observer != null) observer.OnError(error);         else this.Dispose();     }      void IObserver<T>.OnNext(T value)     {         var observer = (IObserver<T>)this.reference.Target;         if (observer != null) observer.OnNext(value);         else this.Dispose();     }      public void Dispose()     {         if (!this.disposed)         {             this.disposed = true;             this.subscription.Dispose();         }     } } 
like image 164
dtb Avatar answered Sep 27 '22 18:09

dtb


Ran across this thread a couple years later...just wanted to point forward to the solution identified on Samuel Jack's blog which adds an extension method to IObservable called WeaklySubscribe. It uses an approach of adding a shim between the subject and observer that tracks the target with a WeakReference. That is similar to solutions offered by others for the problem of strong references in event subscriptions, such as in this article or this solution by Paul Stovell. Having for awhile used something based on Paul's approach I like Samuel's solution to weak IObservable Subscribes.

like image 29
DennisWelu Avatar answered Sep 27 '22 19:09

DennisWelu