Skip to content

Bind and OneWayBind never have their bindings disposed? #834

@gordonwatts

Description

@gordonwatts

I finally got an app up and going. ReactiveUI made a lot of it make more sense than it otherwise would have! However, it leaks memory like a... well...a lot. There are lots of images flying around in my app, so a VM or View that sticks around too long can do quite a bit of damage.

Using VS2013's memory analysis tools, I'm working with a Universal app, and using the memory profiler (from the Analyze menu). When I repeatedly navigate to a new VM from my "home page" when a command is "clicked":

        SwitchPages
            .Select(x => MeetingAddress)
            .Where(x => IsMeeting(x))
            .Subscribe(addr =>
            {
                Settings.LastViewedMeeting = addr;
                HostScreen.Router.Navigate.Execute(new MeetingPageViewModel(HostScreen, ConvertToIMeeting(addr)));
            });

and MeetingPageViewModel's xaml.cs file is:

public sealed partial class MeetingPage : Page, IViewFor<MeetingPageViewModel>
{
    public MeetingPage()
    {
        this.InitializeComponent();

        // Bind everything together we need.
        this.Bind(ViewModel, x => x.MeetingTitle, y => y.MeetingTitle.Text);
    }

    /// <summary>
    /// Stash the view model
    /// </summary>
    public MeetingPageViewModel ViewModel
    {
        get { return (MeetingPageViewModel)GetValue(ViewModelProperty); }
        set { SetValue(ViewModelProperty, value); }
    }
    public static readonly DependencyProperty ViewModelProperty =
        DependencyProperty.Register("ViewModel", typeof(MeetingPageViewModel), typeof(MeetingPage), new PropertyMetadata(null));

    object IViewFor.ViewModel
    {
        get { return ViewModel; }
        set { ViewModel = (MeetingPageViewModel)value; }
    }
}

where MeetingTitle is a TextBox (but it can be a TextBlock as well, and use OneWayBind, it doesn't matter), and the ViewModel is:

public class MeetingPageViewModel : ReactiveObject, IRoutableViewModel
{
    public MeetingPageViewModel(IScreen hs, IMeetingRef mRef)
    {
        // Initial default values
        HostScreen = hs;
        _backing = "hi there";
    }


    /// <summary>
    /// Track the home screen.
    /// </summary>
    public IScreen HostScreen { get; private set; }

    /// <summary>
    /// The meeting title
    /// </summary>
    public string MeetingTitle
    {
        get { return _backing; }
        set { this.RaiseAndSetIfChanged(ref _backing, value); }
    }
    string _backing;

    /// <summary>
    /// Where we will be located.
    /// </summary>
    public string UrlPathSegment
    {
        get { return "/meeting"; }
    }
}

I can then navigate to the screen and back 3 or 4 times, and with the analyzer after forcing a garbage collection, I'll see 3 or 4 instances of MeetingPageViewModel (and MeetingPage). If I remove the binding command, then it is correctly garbage collected. I can also bind to a back button (not shown here), but it doesn't matter if I do or not (I use InvokeCommand) - in all cases MeetingPage and MeetingPageViewModel are correctly collected. So it is just the Bind or OneWayBind that doesn't seem to work.

As a test I took the return from Bind and disposed of it. Of course, the binding was gone, but the VM and View were properly garbage collected.

I see this behavior both on Win10 (most recent build) and a fully patched version of Windows 8.

It is almost as if I should collect everything into a Composite Disposable object, and then dispose of that when the page is deactivated or similar. But my impression is I should not need to do that.

Is this a bug by me (in which case I will move it to stack overflow if we can figure out what I did wrong so others won't get stuck with this) or is this some sort of weird reference chain? BTW, I have a VS2013 diagnostic file which I can share if you want, which has a managed heap snap-shot after loading and unloading the page 3 times, so you can see the trace back to root (it wasn't illuminating for me! :-)).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions