Listening to INotifyPropertyChanged change events and memory leaks

Say that I have an object A that implements INotifyPropertyChanged. I am listening to change events in another object (say, a ViewModel) using a standard event subscription:

myObject.PropertyChanged += SomeMethodInViewModel;

Now if I don't know the lifecycle of the ViewModel, I can't unsubscribe from the event. Hence, I will have a memory leak because object A now holds a strong reference to the ViewModel even after the latter is not in use anymore.

What is the best practice for subscribing to INotifyPropertyChanged property change events?

Posts

  • JimBennettJimBennett GBXamarin Team, Insider, University, Developer Group Leader ✭✭✭✭

    Weak events are your friend here!

    http://www.jimbobbennett.io/weakevents-in-pcls/

  • JahnOttoAndersenJahnOttoAndersen NOMember ✭✭

    Thanks @JimBennett ! I have been looking into PropertyChangedEventManager, but it doesn't seem like it is PCL compliant (as you say in your blog post).

  • AllanRitchie-oldAllanRitchie-old CAInsider ✭✭✭

    Use RX extensions with this little extension method

        public static IObservable<R> ToObservable<T, R>(this T target, Expression<Func<T, R>> property) where T : INotifyPropertyChanged {
            var body = property.Body;
            var propertyName = "";
    
            if (body is MemberExpression)
                propertyName = ((MemberExpression)body).Member.Name;
            else if (body is MethodCallExpression)
                propertyName = ((MethodCallExpression)body).Method.Name;
            else
                throw new NotSupportedException("Only use expressions that call a single property or method");
    
            var getValueFunc = property.Compile();
            return Observable.Create<R>(o => {
                var eventHandler = new PropertyChangedEventHandler((s, pce) => {
                    if (pce.PropertyName == null || pce.PropertyName == propertyName)
                        o.OnNext(getValueFunc(target));
                });
                target.PropertyChanged += eventHandler;
                return () => target.PropertyChanged -= eventHandler;
            });
        }
    

    In your viewmodel:

        this.ToObservable(x => x.YourViewModelProperty).Subscribe(x => { ...do something here });
    

    From this, you can also throttle the event and work with other RX stuff. This will also clean itself up when the viewmodel is disposed of.

  • OtaMaresOtaMares DEMember ✭✭

    How about the events/commands bound via xaml. Do these get unbound by xamarin.forms automagically?

  • adamkempadamkemp USInsider, Developer Group Leader mod

    When you null out the BindingContext it will unsubscribe the events for binding. The only event for commands I think is for observing to changes in whether it can execute, and that probably also gets unhooked when the binding changes, which would be triggered again by the change to the BindingContext. So if you null out your BindingContext everything should be fine.

    Also, if the lifetime of your view and view model are the same (i.e., neither one needs to outlast the other) then they can both get collected at the same time. Cycles are ok as long as nothing is being held onto from an active stack frame or from a static.

    I think the original question was more about events between view models, which can cause view models to stay in memory longer than they should, and therefore keep pages in memory longer than they should as well. There may be other things you can do to leak as well.

Sign In or Register to comment.