I have a MonoTouch app that has a UITabBarController, with each of the tabs being a UINavigationController. Some of these wrap a UIViewController which adds a UITableView and a UIToolbar, and others wrap a DialogViewController.
I've not paid much attention to memory / view management thus far (I've been mostly running in the simulator), but as I've started testing on a real device, I've noticed some failures due to low memory conditions (e.g. the app gets terminated, and I discover from my log that DidReceiveMemoryWarning got called prior to this). Other times I notice prolonged pauses in the app's responsiveness that I am assuming are due to a GC cycle.
Thus far I've been assuming that every DialogViewController that I push onto the nav stack will clean up its views and other things it's allocated when I pop it. But I am starting to realize that it's probably not that easy, and that I need to start calling Dispose() on things.
Are there best practices for how to deal with managing resources and memory with MonoTouch and MT.D? Specifically:
- Is it required to call Dispose on a DialogViewController after it's popped? If so, where is it best to do this? (ViewDidUnload? DidReceiveMemoryWarning? destructor?)
- Does the DVC automatically dispose objects like the RootElement that is passed to it or do I need to worry about this? How about UIImages that it loads as part of rendering a table cell (e.g. StyledStringElement)?
- Are there places where I should call GC.Collect() to better space out collections so as to not take a bit hit in responsiveness when a GC does happen?
- Does the generational garbage collector help with the interactivity problems and is it stable enough to use in a production app? (I believe it's still billed as "experimental" in MonoDevelop 3.0.2 / MT 4.3.3)
- What do I need to do in DidReceiveMemoryWarning to reduce the likelihood that iOS will shoot my app? Since each non-visible view controller seems to get this call, I'm assuming that I should clean up that view controller's resources... should I do the same kinds of things I do in ViewDidUnload?
- I don't seem to get my ViewDidUnload called (even after I get a DidReceiveMemoryWarning). In fact I don't recall ever seeing it in my log. If iOS always called my ViewDidUnload after DidReceiveMemoryWarning, I could just do all the cleanup in ViewDidUnload... What is the best way to split cleanup responsibility between ViewDidUnload and DidReceiveMemoryWarning?
I apologize for the general nature of this question - this seems like a good topic for a whitepaper, but I couldn't find any...
Update: to make the question more concrete: after using Instruments and the Xamarin Heapshot profiler, it's clear to me that I'm leaking UIViewControllers when the user pops the navigation stack. Rolf filed a bug for this and it has two dups, so this is a real issue for more than just me. Unfortunately I haven't found a good workaround for the leaked UIViewControllers - I have not found a good place to call Dispose() on them. The natural place to free resources allocated by ViewDidLoad is in the ViewDidUnload message, but it never gets called on the simulator so my memory footprint keeps growing. On the device, I do see DidReceiveMemoryWarning, but I am reluctant to use this as the place to free my viewcontroller and its resources since I am not guaranteed that iOS will actually unload my view, and therefore not guaranteed that my ViewDidLoad will get called again either (leading to a ViewDidAppear which would need to code defensively against situations where its underlying resources were disposed). I'd love to get some advice on how to get out of this mess...
ViewDidUnload
. – Vann