Xamarin.Mac Internals

PDF for offline use

Let us know how you feel about this

Translation Quality


0/250

While much of the time you’ll never have to worry about the internal "magic" of Xamarin.Mac, having a rough understanding of how things works "under the hood" will help in both interpreting existing documentation with a C# lens and debugging issues when they arise.

In your Xamarin.Mac application, your application bridges two worlds. There is the Objective-C based runtime containing instances of native classes (NSString, NSApplication, etc) and there is the C# runtime containing instances of managed classes (System.String, HttpClient, etc). In between these two works Xamarin.Mac creates a two way bridge so you can call methods (selectors) in Objective-C (such as NSApplication.Init) and Objective-C can call you back (like methods on your app delegate). In general, calls into Objective-C are handled transparently via P/Invokes and some runtime code we provide.

Exposing C# classes / methods to Objective-C

However, for Objective-C to call back into your C# objects, we need to expose it in a way that Objective-C can understand. This is done via Register and Export:

[Register ("MyClass")]
public class MyClass : NSObject
{
   [Export ("init")]
   public MyClass ()
   {
   }

   [Export ("run")]
   public void Run ()
   {
   }
}

In this example, the Objective-C runtime will now know about a class called MyClass with selectors called init and run.

In most cases this is an implementation detail you can ignore, as most callbacks you receive will be either via overridden methods on base classic (AppDelegate, Delegates, DataSources) or on Actions you pass into APIs. In all of those cases, Export attributes are not necessary in your code.

Constructor Runthrough

In many cases you’ll need to expose your C# classes construction API to the Objective-C runtime so it can be instantiated from places such as XIB files. Here are the five most common constructors you may see:

// Called when created from unmanaged code
public CustomView (IntPtr handle) : base (handle)
{
   Initialize ();
}

// Called when created directly from a XIB file
[Export ("initWithCoder:")]
public CustomView (NSCoder coder) : base (coder)
{
   Initialize ();
}

// Called from C# to instance NSView with a Frame (initWithFrame)
public CustomView (RectangleF frame) : base (frame)
{
}

// Called from C# to instance NSView without setting the frame (init)
public CustomView () : base ()
{
}

// This is a special case constructor that you call on a derived class when the derived called has an [Export] constructor.
// For example, if you call init on NSString then you don’t want to call init on NSObject.
public CustomView () : base (NSObjectFlag.Empty)
{
}

In general, you should leave the IntPtr and NSCoder constructors that are generated when you create some types such as customer NSViews alone. If Xamarin.Mac needs to call one of these constructors in response to an Objective-C runtime request and you’ve removed it, you will crash inside native code and it may be difficult to figure out exactly the issue.

Memory Management and Cycles

Memory management in Xamarin.Mac is in many ways very similar to Xamarin.iOS. It also is a complex topic, one beyond the scope of this document. Please read the Memory and Performance Best Practices.

Additional Resources

Here are some more detailed explanations of how things work internally:

Xamarin Workbook

If it's not already installed, install the Xamarin Workbooks app first. The workbook file should download automatically, but if it doesn't, just click to start the workbook download manually.