Bugzilla – Bug 1889
UINavigationController leaks view controllers
Last modified: 2013-10-14 06:26:43 EDT
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.
*** Bug 1840 has been marked as a duplicate of this bug. ***
*** Bug 5181 has been marked as a duplicate of this bug. ***
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
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).
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.
Miguel: that is not correct. This still leaks:
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.
We've run into similar issue - especially visible when me modally present
Currently we have to do the hack like
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.