none
Weak subscribe RRS feed

  • General discussion

  • Hi guys,

    Is it possible to subscribe on observable using weakreference?

    Best regards,

    Alexey Zakharov.

    Tuesday, April 13, 2010 4:53 AM

All replies

  • We are open to suggestions in this area, but I'd like to learn first why people like weak reference-based solutions so much. To be honest, I find the non-deterministic behavior of weak references quite surprising. Just this week I broke my head over some code using the SL Binding object that worked well until I refactored it to use lambdas and closures at which point it stopped working, only to discover that apparently the Source property is a weak reference.

    Stated differently, I think it is much easier to reason about explicitly removing subscriptions using disposable returned from Subscribe, rather than trying to guess when the garbage collector will kick in and suddenly the behavior of my app changes.

    As the guidelines say: "Avoid using weak references as an automatic solution to memory management problems. Instead, develop an effective caching policy for handling your application's objects.". Our intent is that returning an IDisposable from Subscribe makes that simpler and more composable compared to using -= for traditional event handlers so that you do not need to resort to weak references.

    Tuesday, April 13, 2010 6:58 AM
  • Thank you Erik. Now it is clear why you avoid it.
    Tuesday, April 13, 2010 8:08 AM
  • Hi, Erik!

     

    There are some rare scenarios in GUI and some other programming when the unsubscription moment is non-determined and using the strong subscription will cause memory and resources leaks. So it's not possible to explicitly control the unsubscription sometimes.

    Maybe some people will be glad if Rx has only one built-in combinator like AsWeakObservable() I've been posted here . It has clear and simple semantics: wrap observable sequence to do not store the strong reference to the subscribing observable (but IDisposable returned on subscription will hold the strong reference) and automatically unsubscribes from the underlying observable sequence when the observer is dead . It allows users to use Weak event pattern with Rx when they has the scenarios I've describes above, but it should be perfectly documented.

    Thank you.

    p.s. Sorry me my English :((

    Tuesday, April 13, 2010 8:15 AM
  • Maybe if Rx had been around when WPF/SL was concived it wouldn't need so many weak references & weak event listeners.
    • Edited by James Miles Wednesday, April 14, 2010 8:05 AM
    Tuesday, April 13, 2010 12:44 PM
  • WPF/SL doesn't eliminate the problems that weak event pattern solves. WPF provides the WeakEventManager class for the same functionality as weak event pattern.
    Tuesday, April 13, 2010 2:55 PM
  • I've got a Silverlight Application which UI is dynamically constructed from user control with viewmodel inside their datacontext. Some usercontrols are remove from visual tree from time to time, but their view model have a subscription to message channels based on Subject class. It is very hard to control when user control is removed from visual tree. That is why i don't know when to dispose subscribtion to the subject used to for viewmodel communication as result viewmodels with subscribtion lives till the application shutdown. In Microsoft P&P Prism they have EventAggregator (http://msdn.microsoft.com/en-us/library/dd458915.aspx) which has the same roles as subject, but by default is using weak reference.

    So I think in this case it would be reasonable to have an ability to subscribe on RX subject using weak reference. Or I'm wrong?

    Monday, April 19, 2010 5:06 AM
  • I think you are perfectly right and your problem is a great example of what kind of problems weak event pattern may helps to solve.

    Maybe there is a place for special ISubject<T> type in Rx for weak subscribing - WeakSubject<T> .

    Monday, April 19, 2010 7:10 PM
  • Definitely something that's needed and should be built-in!
    Tuesday, September 14, 2010 2:06 AM
  • Hi,

    I don't think the designer's intended usage of WeakReference is the same as the proposed usage for disposing of an observable subscription that has gone out of scope (a.k.a., the weak event pattern, but not really ;).

    Here's another quote from the doc that Eric referenced:

    "Weak references are useful for objects that use a lot of memory, but can be recreated easily if they are reclaimed by garbage collection."

    http://msdn.microsoft.com/en-us/library/ms404247(v=VS.90).aspx

    And then it goes on to provide a nice example about a large tree-view hierarchy going out-of-scope, potentially, only temporarily.

    For one thing, "recreated easily" isn't often possible with an observable.  We sometimes have cold observables, which may replay unwanted side-effects.

    Secondly, "if they are reclaimed" shows the intention of the weak pattern.  It's not something to rely on for cleaning up objects that will never be used again; i.e., when an application no longer requires a resource, such as an observable, then the application may unsubscribe to allow the GC to clean it up.  I don't think anybody in this thread has provided a concrete example yet of a situation in which you couldn't easily dispose of an observable subscription when it's no longer needed.

    For actual .NET events, I can see the benefit of the weak event pattern since unsubscribing from an event isn't encapsulated.  It's up to you to keep a reference to both the event source and the event target, which can be a pain.  And sometimes you handle an event with an anonymous method, so you couldn't explicitly unsubscribe even if you wanted to.  Not unsubscribing from an observable, though, just seems like laziness to me ;)

    Alexey, I'm writing a WPF application that relies on UserControls and view models.  In my app, all user controls derive from a class called View (and some more derived, as View<T>).  View implements IDisposable, which clears all command bindings and tries to dispose of its view model, which may or may not implement IDisposable.  When a UserControl/view model pair is swapped in the main UI for another, the app disposes of the view, which disposes of the view model, which explicitly disposes of all observable subscriptions.

    I'd be interested to know of a situation in which you couldn't detect when the UI is switching user controls.

    - Dave


    http://davesexton.com/blog
    • Edited by Dave Sexton Tuesday, September 14, 2010 8:22 PM
    Tuesday, September 14, 2010 1:03 PM
  • None of our view models use user controls.  We entirely use default DataTemplates merged into resource dictionaries/app.xaml with ContentPresenters to put various data members into where they shoudl go in xaml.

     

    works very well, but we really have no way to know when a view model goes away....  When subscribing to data in our view models, we're careful to use weak events/references to not accidently keep view models around longer than necessary.

     

     

    Tuesday, September 14, 2010 6:52 PM
  • Hi Oren,

    > We entirely use default DataTemplates merged into resource dictionaries/app.xaml with ContentPresenters to put various data members
    > into where they shoudl go in xaml.

    I've just done some brief research, but I don't see any interfaces that WPF provides for when it applies a data template to an object so that it can be notified about being loaded or unloaded.  That's surprsing to me.  And it's a shame, if it's true.  In this case, I could see why you'd be forced to use the weak event pattern, although it still seems to me like an abuse of the pattern.  :)

    Alternatively, you might consider creating user controls for the view models that depend upon observables so that you can implement deterministic disposal.

    - Dave


    http://davesexton.com/blog
    Tuesday, September 14, 2010 8:21 PM
  • We actually prefer ViewModels over user controls as they're far easier to test and mock as they don't have inherent dependencies on Appliction, Dispatcher, or anything else.  We've had lots of issues trying to do unit testing with user controls, especially with MSTest as WPF uses too many statics and has thread-affinity whereas MSTest has each method running in a different threadpool thread. 

    It's a far cleaner separation to use datatemplates for that instead. 

    Tuesday, September 14, 2010 9:19 PM
  • I suggest the following implementation

        public class WeakObserver<T> : IObserver<T>
        {
            private readonly WeakReference<IObserver<T>> _target;
    
            #region Ctor
    
            /// <summary>
            /// Initializes a new instance of the <see cref="WeakObserver{T}"/> class.
            /// </summary>
            /// <param name="target">The target.</param>
            /// <exception cref="System.ArgumentNullException">target</exception>
            public WeakObserver(IObserver<T> target)
            {
                #region Validation
    
                if (target == null)
                    throw new ArgumentNullException("target");
    
                #endregion Validation
    
                _target = new WeakReference<IObserver<T>>(target);
            }
    
            #endregion Ctor
    
            #region Target
    
            /// <summary>
            /// Gets the target.
            /// </summary>
            /// <value>
            /// The target.
            /// </value>
            private IObserver<T> Target
            {
                get
                {
                    IObserver<T> target;
                    if (_target.TryGetTarget(out target))
                        return target;
                    return null;
                }
            }
    
            #endregion Target
    
            #region IObserver<T> Members
    
            #region OnCompleted
    
            /// <summary>
            /// Notifies the observer that the provider has finished sending push-based notifications.
            /// </summary>
            public void OnCompleted()
            {
                IObserver<T> target = Target;
                if (target == null)
                    return;
    
                target.OnCompleted();
            }
    
            #endregion OnCompleted
    
            #region OnError
    
            /// <summary>
            /// Notifies the observer that the provider has experienced an error condition.
            /// </summary>
            /// <param name="error">An object that provides additional information about the error.</param>
            public void OnError(Exception error)
            {
                IObserver<T> target = Target;
                if (target == null)
                    return;
    
                target.OnError(error);
            }
    
            #endregion OnError
    
            #region OnNext
    
            /// <summary>
            /// Provides the observer with new data.
            /// </summary>
            /// <param name="value">The current notification information.</param>
            public void OnNext(T value)
            {
                IObserver<T> target = Target;
                if (target == null)
                    return;
    
                target.OnNext(value);
            }
    
            #endregion OnNext
    
            #endregion IObserver<T> Members
        }

        public class WeakObservable<T> : IObservable<T>
        {
            private readonly IObservable<T> _source;
    
            #region Ctor
    
            /// <summary>
            /// Initializes a new instance of the <see cref="WeakObservable{T}"/> class.
            /// </summary>
            /// <param name="source">The source.</param>
            /// <exception cref="System.ArgumentNullException">source</exception>
            public WeakObservable(IObservable<T> source)
            {
                #region Validation
    
                if (source == null)
                    throw new ArgumentNullException("source");
    
                #endregion Validation
    
                _source = source;
            }
    
            #endregion Ctor
    
            #region Subscribe
    
            /// <summary>
            /// Subscribes the specified observer.
            /// </summary>
            /// <param name="observer">The observer.</param>
            /// <returns></returns>
            public IDisposable Subscribe(IObserver<T> observer)
            {
                IObservable<T> source = _source;
                if (source == null)
                    return Disposable.Empty;
                var weakObserver = new WeakObserver<T>(observer);
                IDisposable disp = source.Subscribe(weakObserver);
                return disp;
            }
    
            #endregion Subscribe
        }

        public static class WeakObservableExtensions
        {
            #region AsWeakObservable
    
            /// <summary>
            /// To weak observable.
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="source">The source.</param>
            /// <returns></returns>
            public static IObservable<T> AsWeakObservable<T>(this IObservable<T> source)
            {
                return new WeakObservable<T>(source);
            }
    
            #endregion AsWeakObservable
        }


    Bnaya Eshet

    Friday, April 10, 2015 4:09 PM