Objective-C selektory w środowisku Xamarin.iOS

Język Objective-C jest oparty na selektorach. Selektor to komunikat, który można wysłać do obiektu lub klasy. Selektory wystąpień platformy Xamarin.iOS mapuje na metody wystąpienia, a selektory klas do metod statycznych.

W przeciwieństwie do normalnych funkcji języka C (i takich jak funkcje składowe języka C++) nie można bezpośrednio wywołać selektora przy użyciu funkcji P/Invoke, selektory są wysyłane do Objective-C klasy lub wystąpienia przy użyciu elementuobjc_msgSend Funkcja.

Aby uzyskać więcej informacji na temat komunikatów w programie Objective-C, zapoznaj się z przewodnikiem Apple Working with Objects (Praca z obiektami firmy Apple).

Przykład

Załóżmy, że chcesz wywołać element sizeWithFont:forWidth:lineBreakMode:selektor na .NSString Deklaracja (z dokumentacji firmy Apple) to:

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

Ten interfejs API ma następujące cechy:

  • Zwracany typ jest CGSize przeznaczony dla ujednoliconego interfejsu API.
  • Parametr font jest interfejsem UIFont (i typem (pośrednio) pochodzącym z obiektu NSObject i jest mapowany na System.IntPtr.
  • Parametr width , CGFloatjest mapowany na nfloat.
  • Parametr lineBreakMode , UILineBreakModezostał już powiązany w środowisku Xamarin.iOS jako UILineBreakMode Wyliczenie.

Łącząc to wszystko, deklaracja powinna być zgodna objc_msgSend z następującymi elementami:

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

Zadeklaruj je w następujący sposób:

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

Aby wywołać tę metodę, użyj kodu, takiego jak:

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

Gdyby zwrócona wartość była strukturą o rozmiarze mniejszym niż 8 bajtów (na przykład starszym SizeF używanym przed przełączeniem do ujednoliconych interfejsów API), powyższy kod zostałby uruchomiony na symulatorze, ale uległby awarii na urządzeniu. Aby wywołać selektor, który zwraca wartość mniejszą niż 8 bitów rozmiaru, zadeklaruj objc_msgSend_stret funkcję:

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

Aby wywołać tę metodę, użyj kodu, takiego jak:

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

Wywoływanie selektora

Wywoływanie selektora obejmuje trzy kroki:

  1. Pobierz element docelowy selektora.
  2. Pobierz nazwę selektora.
  3. Wywołaj objc_msgSend metodę przy użyciu odpowiednich argumentów.

Cele selektora

Obiekt docelowy selektora jest wystąpieniem obiektu lub klasą Objective-C . Jeśli elementem docelowym jest wystąpienie i pochodzi z powiązanego typu Xamarin.iOS, użyj ObjCRuntime.INativeObject.Handle właściwości .

Jeśli element docelowy jest klasą, użyj polecenia ObjCRuntime.Class , aby uzyskać odwołanie do wystąpienia klasy, a następnie użyj Class.Handle właściwości .

Nazwy selektorów

Nazwy selektorów są wymienione w dokumentacji firmy Apple. Na przykład NSString zawiera sizeWithFont: selektory i sizeWithFont:forWidth:lineBreakMode: selektory. Osadzone i końcowe dwukropki są częścią nazwy selektora i nie można ich pominąć.

Po utworzeniu nazwy selektora ObjCRuntime.Selector możesz utworzyć dla niego wystąpienie.

Wywoływanie objc_msgSend

objc_msgSend wysyła komunikat (selektor) do obiektu. Ta rodzina funkcji przyjmuje co najmniej dwa wymagane argumenty: cel selektora (uchwyt wystąpienia lub klasy), selektor sam i wszystkie argumenty wymagane dla selektora. Argumenty wystąpienia i selektora muszą mieć System.IntPtrwartość , a wszystkie pozostałe argumenty muszą odpowiadać typowi oczekiwanego selektora, na przykład nint dla intelementu lub System.IntPtr dla wszystkich NSObjecttypów pochodnych. Użyj interfejsu NSObject.Handle właściwość do uzyskania IntPtr dla Objective-C wystąpienia typu.

Istnieje więcej niż jedna objc_msgSend funkcja:

  • Użyj objc_msgSend_stret dla selektorów, które zwracają strukturę. W usłudze ARM obejmuje to wszystkie typy zwracane, które nie są wyliczaniem ani żadnymi typami wbudowanymi języka C (, , , longint, doublefloat). shortchar W przypadku architektury x86 (symulatora) ta metoda musi być używana dla wszystkich struktur o rozmiarze większym niż 8 bajtów (CGSize jest 8 bajtów i nie jest używana objc_msgSend_stret w symulatorze).
  • Użyj objc_msgSend_fpret dla selektorów, które zwracają wartość zmiennoprzecinkową tylko na x86. Ta funkcja nie musi być używana w usłudze ARM; Zamiast tego użyj polecenia objc_msgSend.
  • Główna funkcja objc_msgSend jest używana dla wszystkich innych selektorów.

Po podjęciu decyzji, które objc_msgSend funkcje należy wywołać (symulator i urządzenie mogą wymagać innej metody), możesz użyć normalnej [DllImport] metody, aby zadeklarować funkcję do późniejszego wywołania.

Zestaw wstępnie wykonanych objc_msgSend deklaracji można znaleźć w pliku ObjCRuntime.Messaging.

Różne wywołania w symulatorze i urządzeniu

Jak opisano powyżej, Objective-C ma trzy rodzaje objc_msgSend metod: jeden dla zwykłych wywołań, jeden dla wywołań, które zwracają wartości zmiennoprzecinkowe (tylko x86) i jeden dla wywołań, które zwracają wartości struktury. Ten ostatni zawiera sufiks _stret w pliku ObjCRuntime.Messaging.

Jeśli wywołujesz metodę, która zwróci niektóre struktury (opisane poniżej reguły), musisz wywołać metodę z wartością zwracaną out jako pierwszy parametr jako wartość:

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

Reguła dotycząca tego, kiedy należy używać _stret_ metody, różni się w przypadku architektury x86 i arm. Jeśli chcesz, aby powiązania działały zarówno na symulatorze, jak i na urządzeniu, dodaj kod, taki jak:

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

Używanie metody objc_msgSend_stret

Podczas kompilowania dla usługi ARM użyj polecenia objc_msgSend_stretdla dowolnego typu wartości, który nie jest wyliczeniem ani żadnym z typów bazowych dla wyliczenia (int, byte, , longshort, double, float).

Podczas kompilowania dla architektury x86 użyj polecenia objc_msgSend_stretdla dowolnego typu wartości, który nie jest wyliczeniem lub żadnym z typów podstawowych dla wyliczenia (int, byte, , longshort, double, float) i którego rozmiar natywny jest większy niż 8 bajtów.

Tworzenie własnych podpisów

Poniższy element może służyć do tworzenia własnych podpisów, jeśli jest to wymagane.