This page is likely outdated (last edited on 07 Sep 2009). Visit the new documentation for updated content.

MonoTouch ManualSelectorInvocation

Table of contents

Objective-C Selectors

The Objective-C language is based upon selectors. A selector is a message that can be sent to an object or a class. MonoTouch maps instance selectors to instance methods, and class selectors to static methods.

Unlike normal C functions (and like C++ member functions), you cannot directly invoke a selector using P/Invoke. (Aside: in theory you could use P/Invoke for non-virtual C++ member functions, but you’d need to worry about per-compiler name mangling, which is a world of pain better ignored.) Instead, selectors are sent to an Objective-C class or instance using the objc_msgSend function.

You may find this helpful guide on Objective-C messaging useful.

Example

Suppose you want to invoke the [-NSString sizeWithFont:forWidth:lineBreakMode:] selector. The declaration (from Apple’s documentation) is:

  • (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode

The return type, GSize, is a System.Drawing.SizeF in managed code (which is a value type).

The font parameter is a UIFont (and a type (indirectly) derived from NSObject), and is thus mapped to System.IntPtr.

The width parameter, a CGFloat, is mapped to System.Single.

The lineBreakMode parameter, a UILineBreakMode, has already been bound in MonoTouch as the UILineBreakMode enumeration.

Put it all together, and we want an objc_msgSend declaration that matches:

SizeF objc_msgSend(IntPtr target, IntPtr selector, IntPtr font, float width, UILineBreakMode mode);

Checking the MonoTouch.ObjCRuntime.Messaging members, we don’t see a match for this prototype. Consequently, we will need to declare it ourself:

[DllImport (MonoTouch.Constants.ObjectiveCLibrary)]
static extern SizeF cgsize_objc_msgSend_IntPtr_float_int (
    IntPtr target, IntPtr selector,
    IntPtr font,
    float width,
    UILineBreakMode mode);

Once declared, we can invoke it once we have the appropriate parameters:

NSString      target = ...
Selector    selector = new Selector ("sizeWithFont:forWidth:lineBreakMode:");
UIFont          font = ...
float          width = ...
UILineBreakMode mode = ...
 
SizeF size = cgsize_objc_msgSend_IntPtr_float_int(
    target.Handle, selector.Handle,
    font == null ? IntPtr.Zero : font.Handle,
    width,
    mode);

Invoking a Selector

Invoking a selector has three steps:

  1. Get the selector target.
  2. Get the selector name.
  3. Call objc_msgSend() with the appropriate arguments.

Selector Targets

A selector target is either an object instance or an Objective-C class. If the target is an instance and came from a bound MonoTouch type, use the MonoTouch.ObjCRuntime.INativeObject.Handle property.

If the target is a class, use MonoTouch.ObjCRuntime.Class to get a reference to the class instance, then use the Class.Handle property.

Selector Names

Selector names are listed within Apple’s documentation. For example, the http://developer.apple.com/iphone/library/documentation/UIKit/Reference/NSString_UIKit_Additions/Reference/Reference.html UIKit NSString extension methods] include sizeWithFont: and sizeWithFont:forWidth:lineBreakMode:. The embedded and trailing colons are important, and are part of the selector name.

Once you have a selector name, you can create a MonoTouch.ObjCRuntime.Selector instance for it.

Calling objc_msgSend()

When invoking objc_msgSend(), you must pass the selector target (an instance or class handle), the selector, and any arguments required by the selector. To do so, you use a normal P/Invoke declaration for objc_msgSend(). The instance and selector arguments must be System.IntPtr, and all remaining arguments must match the type the selector expects.

Objective-C types (e.g. NSString, NSDictionary, UIView, anything that has NSObject as an eventual base type) are passed as System.IntPtr.

A set of pre-made objc_msgSend() declarations can be found in MonoTouch.ObjCRuntime.Messaging.