Reputation: 33058
there are two scenarios I keep wondering about when using Monotouch. They deal with the requirement to keep a reference to a UIViewController
or UIView
.
Example one (I found that in the MT bug database). It is implementing a MKMapViewDelegate
and the view returned from GetViewForAnnotation
will be garbage collected unless a managed reference is kept:
class MapViewDelegate : MKMapViewDelegate
{
public override MKAnnotationView GetViewForAnnotation (MKMapView mapView, NSObject annotation)
{
var myAnnotation = annotation as MyAnnotation;
var view = new MKPinAnnotationView (myAnnotation, "myannotation");
var button = UIButton.FromType (UIButtonType.DetailDisclosure);
view.RightCalloutAccessoryView = button;
// I have had this fail as "invalid selector", and also a SIGSEGV crash
button.TouchDown += delegate {
Console.WriteLine("I was touched!");
};
annotationView = view;
return annotationView;
}
}
Example two: if I use PresentModalViewController()
I seem to be able to survive without holding a managed reference:
ModalSettingsController oSettingsController = new ModalSettingsController ( );
this.PresentModalViewController(oSettingsController, true);
I have never encountered a NULL reference, SIGSEGV or anything like that in this case. It looks like in thise case "something" is keeping a reference.
The problem I have is: if I was to use a pure C# environment wihout ObjC world underneath, the code from example one would work without issues; the view has been created and assigned to something else, meaning, a reference exists. But as the view returned in the first example goes back to ObjC and has no managed reference, the GC will clean the managed version away. This puzzles me a bit. How can I figure out in which cases I have to keep a reference and when not? Do I have to look at Apple's docs and see if the receviver retains the view or controller?
Upvotes: 2
Views: 328
Reputation: 43553
Do I have to look at Apple's docs and see if the receviver retains the view or controller?
No. In general (> 98% of the API) MonoTouch will deal with the required references to let your managed object live as long as required. This can be done because your calling some managed (C#) code from your application. E.g.
this.PresentModalViewController(oSettingsController, true);
This allow the PresentModalViewController managed method to keep a reference (if needed) to oSettingsController for as long as needed.
So what's the issue with some of the API ? The problem is who's calling the API. E.g.
public override MKAnnotationView GetViewForAnnotation (MKMapView mapView, NSObject annotation)
If this was called form C# then it would not be an issue, i.e. your statement wrt pure C# is mostly correct.
However it's called from ObjectiveC since we override a method from a inherited native type. So in this case the managed object view has no (managed) reference (unless you add one, like your example #1) once the method return. But the native caller will retain the native part of the view so things will work... until your application tries to get back into managed code.
Since the managed peer, e.g. view, has been collected then everything managed inside it will be collected too (e.g. button). That's why the crash often occurs in events, e.g. in the delegate you assigned to TouchDown.
That being said we're working on a solution to cover this case as well in future versions of MonoTouch. Add yourself on c.c. of the bug report if you wish to get notified of the progress.
Upvotes: 1