I have observed something that can affect any program's memory consumption and I would like some thoughts.
I have created a very simple test project with a UIViewController and a UINavigationViewController. I push my ViewController and then I pop it. The GC does it job and my ViewController is released (the destructor is called). But, if I create a UIButton and I registered to one of its event (ex: TouchInsideUp), then my ViewController is not released. I need to unregistered to the event in order to release my ViewController. To be sure it was not a timing problem, my test application has a button which calls GC.Collect().
What I don't understand is that an object will be kept alive if it is accessible from the stack of any thread or by a static variable. If my ViewController is eligible for garbage collection, then the UIButton will also be. The event should not cause the ViewController to be kept in memory, because UIButton is not reachable by the GC. In my case, the ViewController is only used by the NavigationController, so once it is popped it should always be collected.
With the help of the new profiler (mono 2.10) I will maybe find a logic answer, but for now, I'm perplex. Any idea?
Edited: Here's some code to help understand my case.
My test ViewController is pretty simple
public class TestViewController : UIViewController{
~TestViewController(){ Console.WriteLine("Finalizer called"); }
public UIButton Button {get; set;}
public override ViewDidLoad(){
base.ViewDidLoad();
// If I remove the event registering, my TestViewController is collected.
Button = new UIButton();
Button.TouchUpInside += ButtonTouchEventHandler;
View.AddSubview(Button);
}
void ButtonTouchEventHandler(object sender, EventArgs e){}
}
My MainWindow has a NavigationController and it does the following:
- It pushed a new instance of TestViewController (thus only the NavigationController has a reference to the TestViewController instance)
- TestViewController is popped via the standard back button (if I don't register to TouchUpInside, TestViewController's finalizer gets called)
- When I return to the MainWindow, a button allows me to call GC.Collect just to be sure.