Bug 1889 - UINavigationController leaks view controllers
Summary: UINavigationController leaks view controllers
Alias: None
Product: iOS
Classification: Xamarin
Component: XI runtime (show other bugs)
Version: 4.x
Hardware: PC Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Bugzilla
: 1840 5181 (view as bug list)
Depends on:
Reported: 2011-11-04 19:18 UTC by Rolf Bjarne Kvinge [MSFT]
Modified: 2014-04-21 06:16 UTC (History)
15 users (show)

See Also:
Is this bug a regression?: ---
Last known good build:

zipped test project (3.61 KB, application/zip)
2011-11-04 19:18 UTC, Rolf Bjarne Kvinge [MSFT]

Description Rolf Bjarne Kvinge [MSFT] 2011-11-04 19:18:11 UTC
Created attachment 827 [details]
zipped test project

See attached test case (comment out the call to FixCase3 (which is this bug) in FinishedLaunching to repro). Run the app, then tap twice on the back button. Notice how only one view[controller] is freed in the application output.

Basically it pushes view controllers in code, but when the user pops them in the ui the popped view controllers aren't freed. This is because UINavigationController fetches the ViewControllers property every time the property changes through managed code, but in this case it's changed through native code, so the managed array isn't updated and we end up with the popped viewcontroller still reachable by the gc.
Comment 1 Rolf Bjarne Kvinge [MSFT] 2012-02-22 19:00:27 UTC
*** Bug 1840 has been marked as a duplicate of this bug. ***
Comment 2 Rolf Bjarne Kvinge [MSFT] 2012-05-18 17:16:51 UTC
*** Bug 5181 has been marked as a duplicate of this bug. ***
Comment 3 Omri Gazitt 2012-06-12 03:32:37 UTC
I can repro this with MonoTouch 5.3.3.

Is there a timeframe for a fix, or perhaps a workaround?  Rolf, I tried reading the ViewControllers.Length property of my UINavigationController by putting this line of code in the ViewDidAppear() override in the first UIViewController that gets pushed onto my nav stack... with the intention that it will cause any UIViewControllers that were pushed and subsequently popped to get GC'ed.  

            int count = this.NavigationController.ViewControllers.Length;

No dice - HeapShot still shows those UIViewControllers as live. 

I have not tried a timer on a separate thread like in the project you enclosed - I sure hope there's a more straightforward workaround (or a fix in the works!)
Comment 4 Rolf Bjarne Kvinge [MSFT] 2012-09-05 12:41:44 UTC
Omri: for now I don't know about any workarounds besides the timer one.

In any case this is unlikely to cause any real-world problems (the view controller will just be freed later when you push another one).
Comment 5 Miguel de Icaza [MSFT] 2012-09-05 17:34:10 UTC
What happens is that the child MyView keeps a reference to the

The MyView itself is added to the MyViewController as its View, which creates
two references: one from the managed MYView to the unmanaged MyView.   The
second comes from the GCHandle one that we create when the
MyViewController.View is assigned.

So we have an unmanaged object keeping our MyView alive, which keeps
MyViewController alive, which in turn has a reference to the MyView.

So this is a cycle.

The workaround is to not have the MyView reference the MyViewController.
Comment 6 Rolf Bjarne Kvinge [MSFT] 2013-06-03 17:51:36 UTC
Miguel: that is not correct. This still leaks: https://gist.github.com/rolfbjarne/9a86e8113d3be689589c

The issue is that we keep a stack of pushed view controllers, but we're not notified when the view controllers are popped, so they stay in that stack until either a new view controller is pushed or popped (programmatically) or the user fetches the ViewControllers array.

Unfortunately I haven't found a way to be notified when the user pops a view controller, so I'm not sure this is even fixable by any reasonable means.
Comment 7 Maciej Czechowski 2013-08-14 06:16:14 UTC
We've run into similar issue - especially visible when me modally present navigation controller.
Currently we have to do the hack like modal.NavigationController.SetViewControllers(new UIViewController[0],false) 
to make our presented controllers gc'able.

Maybe you could re-read the cached properties when the gc is going? This way no obsolete references would be held.
Comment 8 Adam Kemp 2014-03-15 02:13:06 UTC
We are also hitting this, and it is similar to this bug: https://bugzilla.xamarin.com/show_bug.cgi?id=18408.

A workaround is to use the DidShowViewController method of the Delegate like this:

      public void DidShowViewController(UINavigationController navigationController, UIViewController viewController, bool animated)
         // Force the __mt_ViewControllers_var field to be updated.
         var vc = navigationController.ViewControllers;

Needless to say, that's a stupid thing to have to do every time you use a UINavigationController.

This is the second similar leaky behavior I have found today related to these private cache fields. I don't fully understand what they are for (is this how we're keeping C# objects in memory? I thought the runtime had a smarter mechanism for that), but it seems destined to cause leaks. Avoiding leaks in Xamarin is a black art at this point because of crazy little behaviors like this.
Comment 9 Miguel de Icaza [MSFT] 2014-03-15 16:55:28 UTC

We agree, this is a limitation of the current design.

We have addressed this, and the upcoming beta will include a new memory management system that avoids this problem once and for all.   It is only a few weeks away.
Comment 10 Adam Kemp 2014-03-15 18:09:04 UTC
Hooray! Will it work like Xamarin.Android's trick with the weak reference swap trick?
Comment 11 Miguel de Icaza [MSFT] 2014-03-15 21:58:45 UTC
It is a different system, it is inspired by a lot of the work on "SGen/Enable New Ref Count".

We will post more details as we get close to the release.
Comment 12 Rolf Bjarne Kvinge [MSFT] 2014-04-10 09:50:40 UTC
This issue is resolved when using the New-Refcount (NRC) feature [1]. This is not a new feature [2] but it has totally revamped [3] in the last two months based on the bug reports, like this one.

With XI 7.2.1 this feature works with both Boehm (default) and Sgen garbage collectors. NRC has also been enhanced and, right now, there are no known issues (bugs) against it.

While NRC is not the default option (in 7.2.1) we plan to make it so in the near future. Additional testing and feedback on the feature would be appreciated.

Also note that while the test case still expose leaks (MyView[Controller] 2 and 4), those are due to circular references crossing the native/managed boundary, which is not what this bug report is about.

[1] http://docs.xamarin.com/guides/ios/advanced_topics/newrefcount/
[2] http://docs.xamarin.com/releases/ios/MonoTouch_5/MonoTouch_5.2/
[3] Newer versions of Xamarin Studio will remove the experimental tag on the feature when XI 7.2.1+ is used.
Comment 13 BB 2014-04-18 16:06:12 UTC
Great news if this is fixed. Any idea what release it will be in?

Comment 14 BB 2014-04-18 16:09:15 UTC
I mean when the release with it in all be
Comment 15 Rolf Bjarne Kvinge [MSFT] 2014-04-21 06:16:33 UTC
@BB: it's in the 7.2.1 release, which is currently in beta.

Note You need to log in before you can comment on or make changes to this bug.