This is a follow on from here http://forums.xamarin.com/discussion/12005/uiviewcontroller-never-garbage-collected-with-derived-uiview in which I'm trying to sort out a bunch of issues relating to view controllers not getting released.
This time, it seems that after accessing UIViewController.NavigationController you must Dispose the returned object. For example:
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
if (false)
{
// This works
using (var nav = NavigationController)
{
float headerHeight = nav.NavigationBar.Frame.Bottom;
}
}
else
{
// This causes the view controller to not be GC'ed.
float headerHeight = this.NavigationController.NavigationBar.Frame.Bottom;
}
}
Really? Surely it shouldn't be necessary to Dispose the return value of a property get? Is there a better workaround?
See below for a full project that demonstrates the issue:
using System;
using System.Collections.Generic;
using System.Linq;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using System.Drawing;
namespace gctest
{
class TypedWeakReference<T>
{
public TypedWeakReference(T target)
{
_wr = new WeakReference(target);
}
public T Target
{
get { return (T)_wr.Target; }
}
WeakReference _wr;
}
class OtherViewController : UIViewController
{
public OtherViewController()
{
this.View = new UIView();
this.View.BackgroundColor = UIColor.Green;
var button = new UIButton(new RectangleF(20, 40, 280, 60));
button.SetTitle("Dismiss This", UIControlState.Normal);
button.BackgroundColor = UIColor.Orange;
this.View.AddSubview(button);
var weakThis = new TypedWeakReference<OtherViewController>(this);
button.TouchUpInside += (s, a) => {
weakThis.Target.DismissViewController(true, null);
};
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
if (false)
{
// This works
using (var nav = NavigationController)
{
float headerHeight = nav.NavigationBar.Frame.Bottom;
}
}
else
{
// This causes the view controller to not be GC'ed.
float headerHeight = this.NavigationController.NavigationBar.Frame.Bottom;
}
}
~OtherViewController()
{
Console.WriteLine("OtherViewController finalized");
}
}
class MainViewController : UIViewController
{
public MainViewController()
{
this.View.BackgroundColor = UIColor.Yellow;
var button = new UIButton(new RectangleF(20, 40, 280, 60));
button.SetTitle("Show Other", UIControlState.Normal);
button.BackgroundColor = UIColor.Orange;
button.TouchUpInside += (sender, e) => {
var vc = new OtherViewController();
PresentViewController(new UINavigationController(vc), true, null);
};
this.View.AddSubview(button);
button = new UIButton(new RectangleF(20, 140, 280, 60));
button.SetTitle("GC Now", UIControlState.Normal);
button.BackgroundColor = UIColor.Orange;
button.TouchUpInside += (sender, e) => {
System.GC.Collect();
};
this.View.AddSubview(button);
}
}
[Register("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
UIWindow window;
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
window = new UIWindow(UIScreen.MainScreen.Bounds);
window.RootViewController = new MainViewController();
window.MakeKeyAndVisible();
return true;
}
}
}