Switch to a different documentation area.
Objective-C SelectorsTable of contents
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. ExampleSuppose you want to invoke the -[NSString sizeWithFont:forWidth:lineBreakMode:] selector. The declaration (from Apple's documentation) is:
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, EntryPoint="objc_msgSend")]
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);
Unfortunately, while this works under the simulator it will crash on the device (because [DllImport (MonoTouch.Constants.ObjectiveCLibrary, EntryPoint="objc_msgSend_stret")]
static extern void cgsize_objc_msgSend_stret_IntPtr_float_int (
out SizeF retval,
IntPtr target, IntPtr selector,
IntPtr font,
float width,
UILineBreakMode mode);
Invocation now becomes: NSString target = ...
Selector selector = new Selector ("sizeWithFont:forWidth:lineBreakMode:");
UIFont font = ...
float width = ...
UILineBreakMode mode = ...
SizeF size;
if (Runtime.Arch == Arch.SIMULATOR)
size = cgsize_objc_msgSend_IntPtr_float_int(
target.Handle, selector.Handle,
font == null ? IntPtr.Zero : font.Handle,
width,
mode);
else
cgsize_objc_msgSend_stret_IntPtr_float_int(
out size,
target.Handle, selector.Handle,
font == null ? IntPtr.Zero: font.Handle,
width,
mode);
Invoking a SelectorInvoking a selector has three steps:
Selector TargetsA 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 NamesSelector names are listed within Apple's documentation. For example, the 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()
Unfortunately, there is more than one Use objc_msgSend_stret() for selectors which return a structure. To keep things "interesting", on ARM this includes all return types that are not an enumeration or any of the C builtin types (char, short, int, long, float, double). On x86 (the Simulator), this needs to be used for all structures larger than 8 bytes in size. (CGSize -- used in the example above -- is 8 bytes exactly, and thus doesn't use Use objc_msgSend_fpret() for selectors which return a floating point value on x86 only. This function does not need to be used on ARM; instead, use The main objc_msgSend() function is used for all other selectors. Once you've decided which A set of pre-made objc_msgSend() declarations can be found in MonoTouch.ObjCRuntime.Messaging. The UglyObjective-C has three kinds of If you are invoking a method that will return certain structures (rules are described below), you must invoke the method with the return value as the first parameter as an out value, like this: // The following returns a PointF structure: PointF ret; Messaging.PointF_objc_msgSend_stret_PointF_IntPtr (out ret, this.Handle, selConvertPointFromWindow.Handle, point, window.Handle); Things get ugly and because the rule for when you must use _stret is different on X86 and ARM. If you want your bindings to work on both the simulator and the device, you will need to add code that looks like this: if (Runtime.Arch == Arch.DEVICE){
PointF ret;
Messaging.PointF_objc_msgSend_stret_PointF_IntPtr (out ret, myHandle, selector.Handle);
return ret;
} else
return Messaging.PointF_objc_msgSend_PointF_IntPtr (myHandle, selector.Handle);
The rules for when to use the objc_msgSend_stret are like this for ARM:
The rules for when to use the objc_msgSend_stret are like this for X86:
|