MvvmCross icon indicating copy to clipboard operation
MvvmCross copied to clipboard

WindowController from Storyboard gets disposed

Open tofutim opened this issue 8 years ago • 7 comments

I'm still trying to figure out why it happens, but the code to instantiate from Storyboard is flawed in that after some activity, the created WindowController gets disposed. This seems to be resolved by adding a reference in presenters. Taking a look now.

Update. In the Presenter, there is a list of Windows. Is that to prevent premature disposal? Do I need one for the WindowController? I read that GC does not call "dispose" but in my case Dispose is being called.

tofutim avatar Sep 17 '17 16:09 tofutim

The list of Windows is to support multiple windows active at a time. This allows you to show a sheet in a previously shown Window for example.

Maybe we can add a ConditionalWeakTable<NSWindow, NSViewController>?

nmilcoff avatar Sep 17 '17 21:09 nmilcoff

I don't know why WindowController would get Disposed if Window is still alive. Still putzing around with this one. I do not see this in Playground, but I do see it in my "real" app where I do a database call which triggers the Disposal of my ToolbarWindow (MvxWindowController). Not sure why, but I suspect GC. It does not get disposed if I keep a reference of WindowControllers in the Presenter. Odd.

It looks like I can also keep the MvxWindowController from getting disposed by referring to it in my View, e.g.,

        private ToolbarWindow _windowController;
      public override void ViewDidAppear()
        {
            base.ViewDidAppear();

            _windowController = WindowController;

            var set = this.CreateBindingSet<WindowView, WindowViewModel>();
            set.Apply();

            GC.Collect(); 
        }

tofutim avatar Sep 17 '17 21:09 tofutim

What does ConditionalWeakTable do? Would it go in the Presenter?

tofutim avatar Sep 17 '17 22:09 tofutim

I'm guessing that the problem is

                windowController = (MvxWindowController)storyboard.InstantiateControllerWithIdentifier(attribute.WindowControllerName);

which perhaps does not tie the created window with the windowcontroller in a .NET way (in a way that prevents GC from disposing).

tofutim avatar Sep 17 '17 22:09 tofutim

this seems to work

        private ConditionalWeakTable<NSWindow, NSWindowController> weakTable = new ConditionalWeakTable<NSWindow, NSWindowController>();

        protected virtual void ShowWindowViewController(
            NSViewController viewController,
            MvxWindowPresentationAttribute attribute,
            MvxViewModelRequest request)
        {
            ....
            weakTable.Add(windowController.Window, windowController);
        }

Any possible repercussions? Do I have to clear the weakTable when the Window is disposed?

tofutim avatar Sep 17 '17 22:09 tofutim

ConditionalWeakTable will keep the value alive until the key is disposed. Yes, it would be placed in the Presenter.

It's not really necessary to cleanup values, it is done automatically. The drawback is that it consumes much more resources than a Dictionary for example, but I don't think that will be a problem here.

nmilcoff avatar Sep 17 '17 22:09 nmilcoff

Looks like the problem is here again. If I add the forced GC.Collect() in the Playground.Mac.WindowView then the WindowController will be null or managed wrapper will be created from scratch again (so, WindowController itself will not be null, but all the connected outlets there will be null).

Steps to reproduce:

  • get the project from test branch: https://github.com/snechaev/MvvmCross/tree/WindowControllerDisposed-issue2198
  • run Playground.Mac
  • click "Show window"
  • in opened window click "Test" button. You will get the following
    Testing window controller:
    WindowController is not null: True, ok
    Controller creation count: 1, ok
    Controller outlets is not null: True, ok
    
  • click the "GC" button to perform a forced GC.
  • click the "Test" button again. You will get the following;
    Testing window controller:
    WindowController is not null: False, not ok.   <--- problem
    Controller creation count: 1, ok
    Controller outlets is not null: False, not ok.   <--- problem
    

As I can see, the previous fix with the ConditionalWeakTable was removed in the rev. 9c1e34780176785f95a0ee8ae2d4d3fd98665237 by the @nmilcoff. @nmilcoff, do you remember the reasoning behind those changes?

In the current develop we explicitly store the list of the NSWindow-s, but as the NSWindow.windowController property is a weak reference, the NSWindowController will still be GC-ed.

@Cheesebaron, can you please reopen this issue?

snechaev avatar Jun 06 '25 16:06 snechaev

managed wrapper will be created from scratch again (so, WindowController itself will not be null, but all the connected outlets there will be null).

Just for the history: looks like this behavior was related to the runtime implementation where the native code can resolve the reference to the managed object which is in the finalizer queue: https://github.com/dotnet/macios/pull/23072

snechaev avatar Jul 09 '25 15:07 snechaev