Paul Knopf
Paul Knopf

Reputation: 9776

MonoTouch object reference preventing garbage collection

I am having a weird issue that is causing a memory leak in MonoTouch. Here is my setup.

CaseTabController - UITabBarController
-- CaseMediaItemsController - UIViewController
-- CaseInfoController (irrelevant) - UIViewController

The child controllers are displayed as tabs. From the child controllers, I am trying to add some NavigationItem(s) to the parent UITabBarController. However, when I access ParentViewController, a reference is maintained, keeping my objects alive and are never being garbage collected.

Merely adding the following code to the child UIViewController's ViewDidLoad causes the memory leak.

var ni = ParentViewController.NavigationItem;
ni = null;

May parent UITabBarController never get's disposed because my child tabs reference it. Here is the output from HeapShot.

WITH ACCESS TO ParentViewController FROM CHILD TABS

HeapShot with memory leak

WITHOUT ACCESS TO ParentViewController FROM CHILD TABS

HeapShot without memory leak

Note that each memory snapshot was taking after doing the same except steps. Notice how the snapshot WITHOUT reference to ParentViewController how fewer instances, because that are being disposed. They are being disposed of so quickly, that I actually took the snapshot while I was viewing the controllers in question. The snapshot WITHOUT access to ParentViewController was taking while visiting another controller. In each case, the UITabBarController has been popped from the UINavigationController.

Any ideas why the CaseMediaItemsController maintains a reference to the CaseTabController when accessing ParentViewController?

Upvotes: 3

Views: 490

Answers (1)

Stephane Delcroix
Stephane Delcroix

Reputation: 16222

For a complete answer, look at http://xamarin.com/evolve/2013#session-0w86u7bco2

Basically, by referencing the parent in the child, you have a loop, and setting the NavigationItem to null is not enough, you have to Dispose() it as well. But you have to make sure you can, at this point in time, Dispose () the object.

var ni = ParentViewController.NavigationItem;
ni.Dispose ();
ni = null;

You can also go without keeping a strong reference to the NavigationItem, and if you need your ni variable for convenience, make it a WeakReference<UINavigationItem>. That'll allow it to be garbage collected.

WeakReference<UINavigationItem> ni;

public override void ViewDidLoad ()
{
    ni = new WeakReference<UINavigationItem> (ParentViewController.NavigationItem);

    //Nothing else here.
}

You can then use ni like this:

if (ni.Target != null)
    Console.WriteLine (ni.Target.Title);

Upvotes: 6

Related Questions