Objective-C Selektory v Xamarin.iOS
Jazyk Objective-C je založený na Objective-C. Selektor je zpráva, kterou lze odeslat do objektu nebo třídy. Xamarin.iOS mapuje selektory instancí na metody instancí a selektory tříd na statické metody.
Na rozdíl od běžných funkcí jazyka C (stejně jako členské funkce jazyka C++) nelze selektor vyvolat přímo pomocí funkce P/Invoke. Místo toho se selektory odesílané do třídy nebo instance pomocíobjc_msgSend
Funkce.
Další informace o zprávách v najdete Objective-Cv příručce Práce s objekty Objective-C Apple.
Příklad
Předpokládejme, že chcete vyvolatsizeWithFont:forWidth:lineBreakMode:
Selektor na .NSString
Deklarace (z dokumentace společnosti Apple) je následující:
- (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode
Toto rozhraní API má následující vlastnosti:
- Návratový typ je
CGSize
pro Unified API. - Parametr
font
jefont
(a typ (nepřímo) odvozený z objektu NSObject a je namapován na System.IntPtr. - Parametr
width
, je namapovánCGFloat
nanfloat
. - Parametr
lineBreakMode
, ,UILineBreakMode
již byl v Xamarin.iOS svázán jakoUILineBreakMode
Výčtu.
Když všechno dáte dohromady, deklarace objc_msgSend
by se měla shodovat:
CGSize objc_msgSend(
IntPtr target,
IntPtr selector,
IntPtr font,
nfloat width,
UILineBreakMode mode
);
Deklarujte ho následujícím způsobem:
[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
);
K volání této metody použijte například následující kód:
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
);
Pokud by vrácená hodnota byla struktura menší než 8 bajtů ( SizeF
stejně jako starší hodnota použitá před přepnutím na sjednocená rozhraní API), výše uvedený kód by se spouštěl na simulátoru, ale na zařízení se zhroutí. Pokud chcete volat selektor, který vrací hodnotu menší než 8 bitů, deklarujte objc_msgSend_stret
funkci:
[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
);
K volání této metody použijte například následující kód:
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
);
Vyvolání selektoru
Vyvolání selektoru má tři kroky:
- Získejte cíl selektoru.
- Získejte název selektoru.
objc_msgSend
Volejte s příslušnými argumenty.
Cíle selektoru
Cíl selektoru je buď instance objektu, nebo Objective-C třída. Pokud je cílem instance a pochází ze vázaného typu Xamarin.iOS, použijte vlastnost ObjCRuntime.INativeObject.Handle
.
Pokud je cílem třída, použijte k ObjCRuntime.Class
získání odkazu na instanci třídy a pak použijte Class.Handle
vlastnost .
Názvy selektorů
Názvy selektorů jsou uvedené v dokumentaci společnosti Apple. Například zahrnuje NSString
selektory sizeWithFont:
sizeWithFont:forWidth:lineBreakMode:
a . Vložené a koncové dvojtečky jsou součástí názvu selektoru a nelze je vynechat.
Jakmile máte název selektoru, můžete pro něj ObjCRuntime.Selector
vytvořit instanci.
Volání objc_msgSend
objc_msgSend
odešle do objektu zprávu (selektor). Tato skupina funkcí přijímá alespoň dva požadované argumenty: cíl selektoru (popisovač instance nebo třídy), samotný selektor a všechny argumenty požadované pro selektor. Argumenty instance a selektoru System.IntPtr
musí být a všechny zbývající argumenty musí odpovídat typu, který selektor očekává, nint
int
například pro , nebo pro System.IntPtr
NSObject
všechny odvozené typy. Pomocí tlačítkaNSObject.Handle
pro získání IntPtr
instance Objective-C typu.
Existuje více než jedna objc_msgSend
funkce:
- Používá
objc_msgSend_stret
se pro selektory, které vrací strukturou. V ARM to zahrnuje všechny návratové typy, které nejsou výčtem nebo žádným z předdefinových typů jazyka C (char
,short
,int
,long
,float
, ,double
). V x86 (simulátoru) je potřeba tuto metodu použít pro všechny struktury větší než 8 bajtů (CGSize
je 8objc_msgSend_stret
bajtů a v simulátoru se nepoužít). - Používá
objc_msgSend_fpret
se pro selektory, které vracejí pouze hodnotu s plovoucí desetinnou čárkou na x86. Tuto funkci není potřeba používat v ARM. Místo toho použijteobjc_msgSend
. - Hlavní funkce objc_msgSend se používá pro všechny ostatní selektory.
Jakmile se objc_msgSend
rozhodnete, které funkce je potřeba volat (simulátor a [DllImport]
zařízení mohou vyžadovat jinou metodu), můžete pomocí normální metody deklarovat funkci pro pozdější vyvolání.
Sadu předem nastavených deklarací objc_msgSend
lze najít v souboru ObjCRuntime.Messaging
.
Různá volání v simulátoru a zařízení
Jak je popsáno výše, Objective-Cobjc_msgSend
má tři druhy metod: jednu pro běžná volání, jednu pro volání, která vrací hodnoty s plovoucí desetinnou čárkou (pouze x86), a jednu pro vyvolání, která vrací hodnoty struktury. Druhá možnost zahrnuje příponu v _stret
ObjCRuntime.Messaging
.
Pokud voláte metodu, která vrátí určité struktury (pravidla popsaná níže), musíte vyvolat metodu s návratovou hodnotou jako prvním parametrem jako hodnotou 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);
Pravidlo, kdy použít metodu _stret_
, se u x86 a ARM liší.
Pokud chcete, aby vaše vazby fungovaly na simulátoru i na zařízení, přidejte například následující kód:
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);
}
Použití objc_msgSend_stret
Při sestavování pro ARM použijteobjc_msgSend_stret
pro libovolný typ hodnoty, který není výčtem nebo žádným ze základních typů výčtu (int
, byte
, short
, long
, double
, , float
).
Při sestavování pro x86 použijteobjc_msgSend_stret
pro libovolný typ hodnoty, který není výčtem nebo žádným ze základních typů výčtu (int
, byte
, short
, long
, double
, float
) a jehož nativní velikost je větší než 8 bajtů.
Vytváření vlastních podpisů
Následující gist můžete v případě potřeby použít k vytvoření vlastních podpisů.