9

I think I'm getting close to understanding how Mono GC and ObjC ref counting live together.

The way it works is that when a native object has a reference count of 1, we do not prevent the managed instance from getting garbage collected. As soon as the reference count increases above 1, we prevent the managed instance from getting garbage collected.

This is because a managed object may contain user state. For managed objects which are mirroring a corresponding native object (such as the managed UIView instance) MonoTouch knows that the instance can not contain any state, so as soon as no managed code has a reference to the managed instance, the GC can collect it. If a managed instance is required at a later stage, we just create a new one.

So if I create a CustomButton that inherits UIButton, add it as subview to my View, let the managed reference slip out of scope and then run GC, this managed CustomButton still won't be eligible for collection.

Why can't it be collected? Of course it may have managed state like properties, but if there is no link to it from managed objects, who cares about this state? It may as well just disappear, why can't it?

I'm thinking of one possible reason: subscribing to CustomButton events won't keep it alive for the GC so when the object gets collected, events stop firing. This would perhaps result in unexpected behavior.

Is this correct? Are there other reasons for keeping the managed object alive even if no one links it?

Community
  • 1
  • 1
Dan Abramov
  • 264,556
  • 84
  • 409
  • 511

1 Answers1

8

Why can't it be collected? Of course it may have managed state like properties, but if there is no link to it from managed objects, who cares about this state? It may as well just disappear, why can't it?

Native code might have references to the object, which may cause the object to resurface to managed code again later.

I believe a code sample would illustrate what would happen:

class MyView : UIView {
    public string ImportantSecret;
}

class AppDelegate : UIApplicationDelegate {
    UIViewController vc;
    public override bool FinishedLaunching (UIApplication app, 
                                            NSDictionary options)
    {
        var myView = new MyView ();
        myView.ImportantSecret = "MonoTouchRocks";

        vc = new UIViewController ();
        vc.View = new UIView ();
        vc.View.AddSubView (myView);

        // When this method returns the only place where myView is referenced
        // is from inside the *native* Subviews collection.

        BeginInvokeOnMainThread (() =>
        {
            Console.WriteLine (((MyView) vc.Subviews [0]).ImportantSecret);
            // If the MyView instance was garbage collected and recreated
            // automatically at this point, ImportantSecret would be null.
        });
    }
}

Important: this code is just to illustrate the reason why the GC can't collect managed objects which may have state. This particular sample would actually not forget the important secret, since the Subviews array is automatically cached in managed code - but this is not generally true.

Krumelur
  • 32,180
  • 27
  • 124
  • 263
Rolf Bjarne Kvinge
  • 19,253
  • 2
  • 42
  • 86
  • I finally get it! Your support here has been indispensable. – Dan Abramov Oct 25 '12 at 09:30
  • 1
    I love your two questions and Rolf's great answers - just learned something new :-) – Martin Baulig Oct 25 '12 at 20:06
  • I've been playing around with this. What if the myView variable is created inside a using block? The instruments tool indicates that the instances get released. It feels very awkward, but I can't identity and problems in the behavior of the code. Thoughts? @rolf – John Robertson Apr 17 '15 at 22:11