語言Objective-C是以選取器為基礎。 選取器是可以傳送至物件或 類別的訊息。 Xamarin.iOS 會將實例選取器對應至實例方法,並將類別選取器對應至靜態方法。
不同於一般 C 函式(和 C++ 成員函式),您無法使用 P/Invoke 直接叫用選取器,選取器會使用 傳送至Objective-C類別或實例objc_msgSend 功能。
如需 中 Objective-C訊息的詳細資訊,請參閱Apple使用 物件 指南。
範例
假設您想要叫用 sizeWithFont:forWidth:lineBreakMode: 上的 NSString選取器。
宣告(來自蘋果的檔案) 是:
- (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode
此 API 具有下列特性:
- 傳回類型適用於
CGSize整合 API。 - 參數
font是 UIFont (且間接衍生自 NSObject 的類型,且會對應至 System.IntPtr。 - 參數
width是CGFloat,會對應至nfloat。 - 參數
lineBreakMode,已在UILineBreakModeXamarin.iOS 中系結為UILineBreakMode枚舉。
將所有專案放在一起, objc_msgSend 宣告應該相符:
CGSize objc_msgSend(
IntPtr target,
IntPtr selector,
IntPtr font,
nfloat width,
UILineBreakMode mode
);
將它宣告如下:
[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
);
若要呼叫此方法,請使用下列程序代碼:
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 之前使用的舊 SizeF 版本),上述程式代碼會在模擬器上執行,但在裝置上當機。 若要呼叫傳回大小小於 8 位值的選取器,請宣告函 objc_msgSend_stret 式:
[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
);
若要呼叫此方法,請使用下列程序代碼:
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
);
叫用選取器
叫用選取器有三個步驟:
- 取得選取器目標。
- 取得選取器名稱。
- 使用適當的自變數呼叫
objc_msgSend。
選取器目標
選取器目標為物件實例或 Objective-C 類別。 如果目標是實例,而且來自系結的 Xamarin.iOS 類型,請使用 ObjCRuntime.INativeObject.Handle 屬性。
如果目標是類別,請使用 ObjCRuntime.Class 來取得類別實例的參考,然後使用 Class.Handle 屬性。
選取器名稱
選取器名稱會列在 Apple 的檔案中。 例如, NSString 包含 sizeWithFont: 和 sizeWithFont:forWidth:lineBreakMode: 選取器。 內嵌和尾端冒號是選取器名稱的一部分,無法省略。
擁有選取器名稱之後,您可以為其建立 ObjCRuntime.Selector 實例。
呼叫objc_msgSend
objc_msgSend 將訊息 (選取器) 傳送至 物件。 這個函式系列至少接受兩個必要自變數:選取器目標(實例或類別句柄)、選取器本身,以及選取器所需的任何自變數。 實例和選取器自變數必須是 System.IntPtr,而且所有剩餘的自變數都必須符合選取器所預期的類型,例如 nint , int或 System.IntPtr 所有 NSObject衍生型別的 。 使用 NSObject.Handle 要取得 IntPtr 型別 Objective-C 實例的屬性。
有一個 objc_msgSend 以上的函式:
- 用於
objc_msgSend_stret傳回結構的選取器。 在 ARM 上,這包括所有不是列舉型別的傳回型別,或任何 C 內建型別(char、、、intshort、long、doublefloat。 在 x86(模擬器)上,此方法必須用於大小大於 8 個字節的所有結構(CGSize是 8 個字節,不適用於objc_msgSend_stret模擬器)。 - 用於
objc_msgSend_fpret只傳回 x86 上浮點值的選取器。 此函式不需要用於ARM;請改用objc_msgSend。 - 主要 objc_msgSend 函式會用於所有其他選取器。
一旦您決定需要呼叫哪一 objc_msgSend 個函式(模擬器和裝置可能需要不同的方法),您可以使用一般 [DllImport] 方法來宣告函式以供稍後叫用。
您可以在 中找到ObjCRuntime.Messaging一組預先建立objc_msgSend的宣告。
模擬器和裝置上的不同調用
如上所述, Objective-C 有三種 objc_msgSend 方法:一種用於一般調用,一種用於傳回浮點值的調用(僅限 x86),另一種用於傳回結構值的調用。 後者在 中包含ObjCRuntime.Messaging後綴_stret。
如果您要叫用將傳回特定結構的方法(如下所述規則),您必須使用傳回值作為值的第一個 out 參數叫用 方法:
// 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 上有所不同。
如果您想要讓系結同時在模擬器和裝置上運作,請新增程序代碼,如下所示:
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方法
建置 ARM 時,請使用 objc_msgSend_stret表示任何不是列舉型別的實值型別,或列舉型別的任何基底型別(int、、byte、、shortlong、 doublefloat。
建置 x86 時,請使用 objc_msgSend_stret對於不是列舉型別的任何實值型別,或是列舉型別的任何基底型別,intdoublefloatbyteshortlong其原生大小大於8個字節。
建立您自己的簽章
如有需要,您可以使用下列 gist 來建立您自己的簽章。