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 je font (a typ (nepřímo) odvozený z objektu NSObject a je namapován na System.IntPtr.
  • Parametr width , je namapován CGFloatna nfloat.
  • Parametr lineBreakMode , , UILineBreakModejiž 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:

  1. Získejte cíl selektoru.
  2. Získejte název selektoru.
  3. 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.IntPtrmusí být a všechny zbývající argumenty musí odpovídat typu, který selektor očekává, nintintnapříklad pro , nebo pro System.IntPtrNSObjectvš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 8 objc_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žijte objc_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 _stretObjCRuntime.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ů.