Objective-C Xamarin.iOS のセレクターObjective-C selectors in Xamarin.iOS

言語 Objective-C はセレクター に 基づいて行いますThe Objective-C language is based upon selectors. セレクターは、オブジェクトまたはクラス に送信できるメッセージ ですA selector is a message that can be sent to an object or a class. Xamarin.iOS では 、インスタンス セレクターがインスタンス メソッドに、クラス セレクターが静的メソッドにマップされます。Xamarin.iOS maps instance selectors to instance methods, and class selectors to static methods.

通常の C 関数 (および C++ メンバー関数など) とは異なり 、P/Invoke を使用してセレクターを直接呼び出す代わりに、セレクターは を使用してクラスまたはインスタンス Objective-C に送信されます。 objc_msgSendUnlike normal C functions (and like C++ member functions), you cannot directly invoke a selector using P/Invoke Instead, selectors are sent to an Objective-C class or instance using the objc_msgSend 関数。function.

のメッセージの詳細については、Apple の「オブジェクトの操作 Objective-C 」ガイドを参照 してください。For more information about messages in Objective-C, take a look at Apple's Working with Objects guide.

Example

を呼び出すとします。 sizeWithFont:forWidth:lineBreakMode:Suppose you want to invoke the sizeWithFont:forWidth:lineBreakMode: のセレクター NSStringselector on NSString. 宣言 (Apple のドキュメントから) は次の場合です。The declaration (from Apple's documentation) is:

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

この API には、次の特性があります。This API has the following characteristics:

  • 戻り値の型 CGSize は、Unified API。The return type is CGSize for the Unified API.
  • パラメーター fontUIFont (および NSObjectから派生した型 (間接的に) であり 、System.IntPtr にマップされますThe font parameter is a UIFont (and a type (indirectly) derived from NSObject, and is mapped to System.IntPtr.
  • パラメーター width である は CGFloat 、 にマップされます nfloatThe width parameter, a CGFloat, is mapped to nfloat.
  • パラメーター lineBreakMode である UILineBreakMode は、Xamarin.iOS で として既にバインドされている UILineBreakModeThe lineBreakMode parameter, a UILineBreakMode, has already been bound in Xamarin.iOS as the UILineBreakMode 列挙。enumeration.

すべてをまとめる場合、宣言 objc_msgSend は次の条件と一致する必要があります。Putting it all together, the objc_msgSend declaration should match:

CGSize objc_msgSend(
    IntPtr target,
    IntPtr selector,
    IntPtr font,
    nfloat width,
    UILineBreakMode mode
);

次のように宣言します。Declare it as follows:

[DllImport (Constants.ObjectiveCLibrary, EntryPoint="objc_msgSend")]
static extern CGSize cgsize_objc_msgSend_IntPtr_float_int (
    IntPtr target,
    IntPtr selector,
    IntPtr font,
    nfloat width,
    UILineBreakMode mode
);

このメソッドを呼び出す場合は、次のようなコードを使用します。To call this method, use code such as the following:

NSString target = ...
Selector selector = new Selector ("sizeWithFont:forWidth:lineBreakMode:");
UIFont font = ...
nfloat width = ...
UILineBreakMode mode = ...

CGSize size = cgsize_objc_msgSend_IntPtr_float_int(
    target.Handle,
    selector.Handle,
    font == null ? IntPtr.Zero : font.Handle,
    width,
    mode
);

返された値のサイズが 8 バイト未満の構造体 (統合 API に切り替える前に使用された古い構造体など) の場合、上記のコードはシミュレーターで実行されますが、デバイスでクラッシュしました。 SizeFHad the returned value been a structure that was less than 8 bytes in size (like the older SizeF used before switching to the Unified APIs), the above code would have run on the simulator but crashed on the device. サイズが 8 ビット未満の値を返すセレクターを呼び出す場合は、 関数を宣言 objc_msgSend_stret します。To call a selector that returns a value less than 8 bits in size, declare the objc_msgSend_stret function:

[DllImport (MonoTouch.Constants.ObjectiveCLibrary, EntryPoint="objc_msgSend_stret")]
static extern void cgsize_objc_msgSend_stret_IntPtr_float_int (
    out CGSize retval,
    IntPtr target,
    IntPtr selector,
    IntPtr font,
    nfloat width,
    UILineBreakMode mode
);

このメソッドを呼び出す場合は、次のようなコードを使用します。To call this method, use code such as the following:

NSString      target = ...
Selector    selector = new Selector ("sizeWithFont:forWidth:lineBreakMode:");
UIFont          font = ...
nfloat          width = ...
UILineBreakMode mode = ...

CGSize 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 selector

セレクターの呼び出しには、次の 3 つの手順があります。Invoking a selector has three steps:

  1. セレクター ターゲットを取得します。Get the selector target.
  2. セレクター名を取得します。Get the selector name.
  3. 適切 objc_msgSend な引数を使用して を呼び出します。Call objc_msgSend with the appropriate arguments.

セレクター ターゲットSelector targets

セレクター ターゲットは、オブジェクト インスタンスまたはクラス Objective-C のいずれかです。A selector target is either an object instance or an Objective-C class. ターゲットがインスタンスで、バインドされた Xamarin.iOS 型から取得された場合は、 プロパティを使用 ObjCRuntime.INativeObject.Handle します。If the target is an instance and came from a bound Xamarin.iOS type, use the ObjCRuntime.INativeObject.Handle property.

ターゲットがクラスの場合は、 を使用してクラス インスタンスへの参照を ObjCRuntime.Class 取得し、 プロパティを使用 Class.Handle します。If the target is a class, use ObjCRuntime.Class to get a reference to the class instance, then use the Class.Handle property.

セレクター名Selector names

セレクター名は Apple のドキュメントに記載されています。Selector names are listed in Apple's documentation. たとえば、 には NSStringsizeWithFont: セレクター sizeWithFont:forWidth:lineBreakMode: が含まれます。For example, NSString includes sizeWithFont: and sizeWithFont:forWidth:lineBreakMode: selectors. 埋め込みコロンと末尾のコロンはセレクター名の一部であり、省略することはできません。The embedded and trailing colons are part of the selector name and cannot be omitted.

セレクター名を取得したら、そのインスタンス ObjCRuntime.Selector を作成できます。Once you have a selector name, you can create a ObjCRuntime.Selector instance for it.

呼び出objc_msgSendCalling objc_msgSend

objc_msgSend は、メッセージ (セレクター) を オブジェクトに送信します。objc_msgSend sends a message (selector) to an object. この関数ファミリは、少なくとも 2 つの必須引数 (セレクター ターゲット (インスタンスまたはクラス ハンドル)、セレクター自体、およびセレクターに必要な引数を受け取ります。This family of functions takes at least two required arguments: the selector target (an instance or class handle), the selector itself, and any arguments required for the selector. インスタンスとセレクターの引数は である必要があります。残りの引数はすべて、セレクターが期待する型 (たとえば、 の場合) またはすべての派生型の と一致する System.IntPtr nint int System.IntPtr NSObject 必要があります。The instance and selector arguments must be System.IntPtr, and all remaining arguments must match the type the selector expects, for example an nint for an int, or a System.IntPtr for all NSObject-derived types. を使用します。 NSObject.HandleUse the NSObject.Handle 型インスタンスの IntPtr を取得 Objective-C するプロパティ。property to obtain an IntPtr for an Objective-C type instance.

複数の関数 objc_msgSend があります。There is more than one objc_msgSend function:

  • 構造体 objc_msgSend_stret を返すセレクターに使用します。Use objc_msgSend_stret for selectors that return a struct. ARM では、列挙型ではないすべての戻り値の型、または C 組み込み型 ( char short int 、、、、、 ) が longfloat まれます doubleOn ARM, this includes all return types that are not an enumeration or any of the C built-in types (char, short, int, long, float, double). x86 (シミュレーター) では、サイズが 8 バイトを超えるすべての構造体に対してこのメソッドを使用する必要があります ( は 8 バイトであり、シミュレーターでは CGSize objc_msgSend_stret 使用されません)。On x86 (the simulator), this method needs to be used for all structures larger than 8 bytes in size (CGSize is 8 bytes and doesn't use objc_msgSend_stret in the simulator).
  • objc_msgSend_fpretx86 でのみ浮動小数点値を返すセレクターに使用します。Use objc_msgSend_fpret for selectors that return a floating point value on x86 only. この関数は ARM で使用する必要はありません。代わりに、 を使用します objc_msgSendThis function does not need to be used on ARM; instead, use objc_msgSend.
  • 主要な objc_msgSend 関数は、他のすべてのセレクターに使用されます。The main objc_msgSend function is used for all other selectors.

呼び出す必要がある関数 (シミュレーターとデバイスごとに異なるメソッドが必要な場合があります) を決定したら、通常のメソッドを使用して、後で呼び出す関数を宣言 objc_msgSend [DllImport] できます。Once you've decided which objc_msgSend function(s) you need to call (simulator and device may each require a different method), you can use a normal [DllImport] method to declare the function for later invocation.

事前に作成された宣言のセット objc_msgSend は、 で確認できます ObjCRuntime.MessagingA set of pre-made objc_msgSend declarations can be found in ObjCRuntime.Messaging.

シミュレーターとデバイスでのさまざまな呼び出しDifferent invocations on simulator and device

前述のように、 には 3 種類のメソッドがあります。1 つは通常の呼び出し用、もう 1 つは浮動小数点値を返す呼び出し Objective-C (x86 のみ)、もう 1 つは構造体値を返す呼び出し用です。 objc_msgSendAs described above, Objective-C has three kinds of objc_msgSend methods: one for regular invocations, one for invocations that return floating point values (x86 only), and one for invocations that return struct values. 後者には、 のサフィックスが _stret 含まれています ObjCRuntime.MessagingThe latter includes the suffix _stret in ObjCRuntime.Messaging.

特定の構造体 (以下で説明する規則) を返すメソッドを呼び出す場合は、戻り値を値として最初のパラメーターとして使用して メソッドを呼び出す必要 out があります。If you are invoking a method that will return certain structs (rules described below), you must invoke the method with the return value as the first parameter as an out value:

// The following returns a PointF structure:
PointF ret;
Messaging.PointF_objc_msgSend_stret_PointF_IntPtr (out ret, this.Handle, selConvertPointFromWindow.Handle, point, window.Handle);

メソッドを使用する場合の規則 _stret_ は、x86 と ARM で異なります。The rule for when to use the _stret_ method differs on x86 and ARM. シミュレーターとデバイスの両方でバインドを機能する場合は、次のようなコードを追加します。If you want your bindings to work on both the simulator and the device, add code such as the following:

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);
}

objc_msgSend_stret メソッドの使用Using the objc_msgSend_stret method

ARM 用に構築する場合は、 objc_msgSend_stretWhen building for ARM, use the objc_msgSend_stret 列挙型ではない任意の値型、または列挙型の基本型 ( int byte short long double 、、、 float )。for any value type that is not an enumeration or any of the base types for an enumeration (int, byte, short, long, double, float).

x86 用に構築する場合は、 objc_msgSend_stretWhen building for x86, use objc_msgSend_stret 列挙型ではない値型、または列挙型 (、 ) の基本型で、ネイティブ サイズが int byte short long double 8 バイトを超える場合 float は 。for any value type that is not an enumeration or any of the base types for an enumeration (int, byte, short, long, double, float) and whose native size is larger than 8 bytes.

独自の署名を作成するCreating your own signatures

必要に 応じて、 次の gist を使用して独自の署名を作成できます。The following gist can be used to create your own signatures, if required.