Issue with use of NSApplication.SharedApplication.Windows

MichaelBothMichaelBoth AUMember ✭✭
edited September 12 in Xamarin.Mac

Our testers are sometimes experiencing exceptions with the use of our application, with the dump being similar to this:

Message: Exception has been thrown by the target of an invocation.
 Source: mscorlib
  at System.Reflection.MonoCMethod.InternalInvoke (System.Object obj, System.Object[] parameters) [0x00014] in <d4607bd5629a411f90f04d320279ccfd>:0 
  at System.Reflection.MonoCMethod.DoInvoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x0007a] in <d4607bd5629a411f90f04d320279ccfd>:0 
  at System.Reflection.MonoCMethod.Invoke (System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <d4607bd5629a411f90f04d320279ccfd>:0 
  at System.Reflection.ConstructorInfo.Invoke (System.Object[] parameters) [0x00000] in <d4607bd5629a411f90f04d320279ccfd>:0 
  at ObjCRuntime.Runtime.ConstructNSObject[T] (System.IntPtr ptr, System.Type type, ObjCRuntime.Runtime+MissingCtorResolution missingCtorResolution) [0x00045] in <4038e84c0daf42429d15d7404249453d>:0 
  at ObjCRuntime.Runtime.GetINativeObject[T] (System.IntPtr ptr, System.Boolean owns) [0x0012b] in <4038e84c0daf42429d15d7404249453d>:0 
  at Foundation.NSArray.UnsafeGetItem[T] (System.IntPtr handle, System.nuint index) [0x0002a] in <4038e84c0daf42429d15d7404249453d>:0 
  at Foundation.NSArray.ArrayFromHandle[T] (System.IntPtr handle) [0x0003e] in <4038e84c0daf42429d15d7404249453d>:0 
  at AppKit.NSApplication.get_Windows () [0x00022] in <4038e84c0daf42429d15d7404249453d>:0 
  at FeastudyMac.FSMacNavigationWorkFlow.RefreshOpenViews (System.Boolean bForceUpdateDataOnViews) [0x00060] in <4b39ecd6ba8847e5bdbd86ffee365b5b>:0

Chris Hamons writes here:
https://forums.xamarin.com/discussion/81199/closed-windows-are-not-releasing-memory
that

NSApplication.SharedApplication.Windows is EVIL. It hooks into the alloc/dealloc of windows, and when you reference it in C# will make bridged objects to everything in the list. Which means if you have windows that are going to dealloc, and then look at the list, you now poked them again to stay alive a bit longer.

The line of code that appears to be the issue is:
NSWindow [] openWindows = NSApplication.SharedApplication.Windows
... and it's like it's this line itself that is causing the error, which appears to be odd to me.

But then, despite Chris's warning, I look in the Xamarin sample code here:
https://developer.xamarin.com/guides/mac/user-interface/working-with-windows/
... and find NSApplication.SharedApplication.Windows is used.

I am using this property to scan all open application windows, and then update the ones that are still non-disposed and that need data refreshed.

Should or should I not be using the ...Windows property?

Posts

  • ChrisHamonsChrisHamons USXamarin Team Xamurai

    To add color to my previous discussion:

    NSApplication.SharedApplication.Windows is EVIL. It hooks into the alloc/dealloc of windows, and when you reference it in C# will make bridged objects to everything in the list. Which means if you have windows that are going to dealloc, and then look at the list, you now poked them again to stay alive a bit longer.

    The problem at hand was a user who was trying to track down a non-existant leak. In their tracking code, they would use that property, which would refresh the lifetime of all windows, preventing them from every being collected. Thus, the code that looked for leaks caused a leak as long as it was running.

    It is hard to tell from your stack trace, but it appears that we are walking the arrive of Windows, trying to surface one to C#, and it is either disposed of or dies while we're doing it, and thus a crash.

    I am using this property to scan all open application windows, and then update the ones that are still non-disposed and that need data refreshed.

    Should or should I not be using the ...Windows property?

    In general, code that needs to use Windows is already wrong in theory. Your application should know enough state to know what to update without walking the windows. Windows have controller that own some model that you can update, and it bubbles up.

    I can look at the specific issue if you post a small sample, but I'd stay away from Windows if possible. The Apple implementation is evil IMO.

  • I use a global window manager class. It keeps a list of windowcontrollers. Everytime I create a window controller I pass it to the manager.

    To get visible windows I iterate the list and check windowcontoller.Window.IsVisible.

    The window manager has methods to tile all windows on the screen in an arbitrary number of colums

  • MichaelBothMichaelBoth AUMember ✭✭

    Chris - thanks for the info. I did find a couple of outstanding cases of "ReleaseOnClosed" being set to true, but even with this fixed I still had issues. I've now started implementation on a global manager, and so - Sjoerd - thanks also for your input, it's good to know I'm on the right track here. I had used the .Windows array mainly because I'm coming from Windows desktop programming, and there is a similar function available that works pretty well in that ecosystem. Still, a different approach is needed for Xamarin / macOS.

Sign In or Register to comment.