Selettori Objective-C in Xamarin.iOS

  • 07/12/2017
  • 4 minuti a leggere
    • d
    • d
    • c
    • un

Il linguaggio Objective-C è basata su selettori. Un selettore è un messaggio che può essere inviato a un oggetto o una classe. Xamarina.iOS associa i selettori di istanze ai metodi di istanza e ai selettori di classe ai metodi statici.

A differenza delle normali funzioni C (e come le funzioni membro C++), non è possibile richiamare direttamente un selettore utilizzando invece P/Invoke,i selettori vengono inviati a una classe o istanza Objective-C utilizzando la funzioneobjc_msgSend.

Per ulteriori informazioni sui messaggi in Objective-C, dai un’occhiata al lavoro di Apple con Objectsguide.

Esempio

Supponiamo di voler richiamare il selettore sizeWithFont:forWidth:lineBreakMode: su NSString.La dichiarazione (dalla documentazione di Apple) è:

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

Questa API ha le seguenti caratteristiche:

  • Il tipo restituito è CGSize per l’API unificata.
  • Il parametro font è un UIFont (e un tipo (indirettamente) derivato da NSObject, ed è mappato al Sistema.IntPtr.
  • Il parametro width, a CGFloat, è mappato a nfloat.
  • Il parametro lineBreakMode,a UILineBreakMode, è già stato associato a Xamarin.iOS come enumerazioneUILineBreakMode.

Mettendo tutto insieme, la dichiarazione objc_msgSend dovrebbe corrispondere:

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

Dichiararlo come segue:

static extern CGSize cgsize_objc_msgSend_IntPtr_float_int ( IntPtr target, IntPtr selector, IntPtr font, nfloat width, UILineBreakMode mode);

Per chiamare questo metodo, utilizzare il codice come il seguente:

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

Se il valore restituito fosse una struttura di dimensioni inferiori a 8 byte (come il precedente SizeF utilizzato prima di passare alle API unificate), il codice precedente sarebbe stato eseguito sul simulatore ma si è bloccato sul dispositivo. Per chiamare un selettore che restituisce un valore inferiore a 8 bit, dichiarare la objc_msgSend_stret funzione:

static extern void cgsize_objc_msgSend_stret_IntPtr_float_int ( out CGSize retval, IntPtr target, IntPtr selector, IntPtr font, nfloat width, UILineBreakMode mode);

chiamare questo metodo, utilizzare il codice come il seguente:

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

il richiamo di un selettore

Invocare un selettore a tre passi:

  1. Ottenere il selettore di destinazione.
  2. Ottieni il nome del selettore.
  3. Chiama objc_msgSend con gli argomenti appropriati.

Target di selezione

Un target di selezione è un’istanza oggetto o una classe Objective-C. Ifthe target è un’istanza e proviene da un Xamarin associato.Tipo iOS, utilizzare la proprietà ObjCRuntime.INativeObject.Handle.

Se la destinazione è una classe, utilizzare ObjCRuntime.Class per ottenere un riferimento a classinstance, quindi utilizzare la proprietà Class.Handle.

Nomi dei selettori

I nomi dei selettori sono elencati nella documentazione di Apple. Ad esempio, NSString include sizeWithFont: e sizeWithFont:forWidth:lineBreakMode: selettori. I due punti incorporati e finali fanno parte del nome del selettore e non possono essere omessi.

Una volta che hai un nome selettore, puoi creare un’istanza ObjCRuntime.Selector per esso.

Chiamare objc_msgSend

objc_msgSend invia un messaggio (selettore) a un oggetto. Questa famiglia di funzioni richiede almeno due argomenti richiesti: la destinazione del selettore (aninstance o handle di classe), il selettore stesso e qualsiasi argumentsrequired per il selettore. Gli argomenti dell’istanza e del selettore devono essereSystem.IntPtr e tutti gli argomenti rimanenti devono corrispondere al tipo previsto da theselector, ad esempio un nint per un int o unSystem.IntPtrper tutti i tipi derivati da NSObject. Utilizzare la proprietà NSObject.Handleper ottenere un IntPtr per un’istanza di tipo Objective-C.

C’è più di una funzione objc_msgSend :

  • Utilizzare objc_msgSend_stret per i selettori che restituiscono una struttura. Su ARM, questo include tutti i returntypes che non sono un’enumerazione o uno qualsiasi dei tipi C incorporati(char,short, int, long, float, double). Su x86 (il simulatore), questo metodo deve essere utilizzato per tutte le strutture di dimensioni superiori a 8 byte (CGSize è 8 byte e non usa objc_msgSend_stret nel simulatore).
  • Utilizzare objc_msgSend_fpret per i selettori che restituiscono un valore in virgola mobile solo su x86. Thisfunction non ha bisogno di essere usato su ARM; invece, usa objc_msgSend.
  • mainobjc_msgSendfunction viene utilizzato per tutti gli altri selettori.

Una volta che hai deciso quali funzioni objc_msgSend devi chiamare(il simulatore e il dispositivo potrebbero richiedere un metodo diverso), puoi usareun metodo normale per dichiarare la funzione per una successiva invocazione.

Un insieme di dichiarazioni objc_msgSendpre-fatte può essere trovato inObjCRuntime.Messaging.

Diverse invocazioni sul simulatore e sul dispositivo

Come descritto sopra, Objective-C ha tre tipi di metodi objc_msgSend: uno per le invocazioni regolari, uno per le invocazioni che restituiscono i valori del punto di galleggiamento (solo x86) e uno per le invocazioni che restituiscono i valori di struct. Quest’ultimo include il suffisso _stret inObjCRuntime.Messaging.

Se si invoca un metodo che restituirà determinate strutture (rulesdescribed below), è necessario invocare il metodo con il valore restituito come firstparameter come valore 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);

La regola per quando utilizzare il metodo _stret_ differisce su x86 e ARM.Se si desidera che le associazioni al lavoro sia sul simulatore e il dispositivo,aggiungere il codice come il seguente:

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

Utilizzando il objc_msgSend_stret metodo

Quando si costruisce per il BRACCIO, utilizzareobjc_msgSend_stretper qualsiasi tipo di valore, che non è un’enumerazione o di qualsiasi base typesfor un’enumerazione (int, byte, short, long, double, float).

Quando si crea per x86, utilizzareobjc_msgSend_stret per qualsiasi tipo di valore che non sia un’enumerazione o uno qualsiasi dei tipi di baseper un’enumerazione (int, byte, short, long, double, float)e la cui dimensione nativa è maggiore di 8 byte.

Creare le proprie firme

Il seguente gist può essere utilizzato per creare le proprie firme, se necessario.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.