Sdílet prostřednictvím


Objective-C selektory v Xamarin.iOS

Jazyk Objective-C je založený na selektorech. Selektor je zpráva, která se dá 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 normálních funkcí jazyka C (a podobně jako členské funkce jazyka C++) nelze přímo vyvolat selektor pomocí volání nespravovaného volání, selektory se odesílají do Objective-C třídy nebo instance pomocíobjc_msgSend Funkce.

Další informace o zprávách najdete Objective-Cv příručce Práce s objekty společnosti Apple.

Příklad

Předpokládejme, že chcete vyvolat sizeWithFont:forWidth:lineBreakMode: selektor zapnutý NSString. Deklarace (z dokumentace společnosti Apple) je:

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

Toto rozhraní API má následující charakteristiky:

  • Návratový typ je CGSize určený pro sjednocené rozhraní API.
  • Parametr font je UIFont (a typ (nepřímo) odvozený z NSObject a je mapován na System.IntPtr.
  • Parametr width , a CGFloat, je namapován na nfloat.
  • Parametr lineBreakMode , a UILineBreakMode, již byl vázán v Xamarin.iOS jako UILineBreakMode Výčtu.

Všechny deklarace by se měly shodovat objc_msgSend :

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

Deklarujte ji 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
);

Chcete-li volat tuto metodu, použijte kód, například následující:

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

Byla-li vrácená hodnota strukturou menší než 8 bajtů (podobně jako starší SizeF použitá před přepnutím na sjednocená rozhraní API), výše uvedený kód by běžel na simulátoru, ale v zařízení došlo k chybovému ukončení. Chcete-li volat selektor, který vrátí hodnotu menší než 8 bitů velikosti, 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
);

Chcete-li volat tuto metodu, použijte kód, například následující:

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. Volání objc_msgSend s příslušnými argumenty

Cíle selektoru

Cíl selektoru je buď instance objektu Objective-C , nebo třída. Pokud je cílem instance a pochází z vázaného typu Xamarin.iOS, použijte ObjCRuntime.INativeObject.Handle vlastnost.

Pokud je cílem třída, použijte ObjCRuntime.Class k 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. Zahrnuje například NSStringsizeWithFont: a sizeWithFont:forWidth:lineBreakMode: selektory. Vložené a koncové dvojtečky jsou součástí názvu selektoru a nelze je vynechat.

Jakmile budete mít název selektoru, můžete pro něj vytvořit ObjCRuntime.Selector instanci.

Volání objc_msgSend

objc_msgSend odešle zprávu (selektor) objektu. Tato řada funkcí má alespoň dva požadované argumenty: cíl selektoru (instance nebo popisovač třídy), samotný selektor a všechny argumenty požadované pro selektor. Argumenty instance a selektoru musí být System.IntPtra všechny zbývající argumenty musí odpovídat typu, který selektor očekává, například nint pro intSystem.IntPtr nebo pro všechny NSObjectodvozené typy. Použít NSObject.Handle vlastnost k získání IntPtr instance Objective-C typu.

Existuje více než jedna objc_msgSend funkce:

  • Slouží objc_msgSend_stret pro selektory, které vrací strukturu. V ARM to zahrnuje všechny návratové typy, které nejsou výčtem nebo žádným z předdefinovaných typů jazyka C (char, , shortint, long, float, ). double V x86 (simulátoru) musí být tato metoda použita pro všechny struktury větší než 8 bajtů velikosti (CGSize je 8 bajtů a nepoužívá objc_msgSend_stret se v simulátoru).
  • Slouží objc_msgSend_fpret pouze pro selektory, které vracejí hodnotu s plovoucí desetinou čárkou pouze u x86. Tuto funkci není nutné používat v ARM; místo toho použijte objc_msgSend.
  • Hlavní objc_msgSend funkce se používá pro všechny ostatní selektory.

Jakmile se rozhodnete, které objc_msgSend funkce potřebujete volat (simulátor a zařízení můžou vyžadovat jinou metodu), můžete pomocí normální [DllImport] metody deklarovat funkci pro pozdější vyvolání.

Soubor předpřipravených objc_msgSend deklarací lze nalézt v ObjCRuntime.Messagingčásti .

Různé vyvolání na simulátoru a zařízení

Jak je popsáno výše, Objective-C má tři druhy objc_msgSend metod: jeden pro pravidelné vyvolání, jeden pro vyvolání, které vracejí hodnoty s plovoucí desetinou čárkou (pouze x86) a jeden pro vyvolání, které vracejí hodnoty struktury. Druhá zahrnuje příponu _stret v ObjCRuntime.Messaging.

Pokud voláte metodu, která vrátí určité struktury (pravidla popsaná níže), musíte metodu vyvolat s návratovou out hodnotou jako první parametr jako hodnotu:

// 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 liší v x86 a ARM. Pokud chcete, aby vazby fungovaly na simulátoru i na zařízení, přidejte kód, například následující:

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í metody objc_msgSend_stret

Při sestavování pro ARM použijte objc_msgSend_stretpro libovolný typ hodnoty, který není výčtem nebo žádným ze základních typů výčtu (int, byte, , longshort, double). float

Při sestavování pro x86 použijte objc_msgSend_stretpro libovolný typ hodnoty, který není výčtem nebo žádným ze základních typů výčtu (int, , , longshort, ) doublefloata jehož nativní velikost je větší než 8 bytebajtů.

Vytváření vlastních podpisů

V případě potřeby můžete k vytvoření vlastních podpisů použít následující gist .