Objective-C selettori in Xamarin.iOS
La Objective-C lingua si basa sui selettori. Un selettore è un messaggio che può essere inviato a un oggetto o a una classe. Xamarin.iOS esegue il mapping dei selettori di istanza 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 usando P/Invoke , i selettori vengono inviati a una classe o a un'istanza Objective-C usando objc_msgSend
Funzione.
Per altre informazioni sui messaggi in Objective-C, vedere la guida Sull'uso degli oggetti di Apple.
Esempio
Si supponga di voler richiamare il sizeWithFont:forWidth:lineBreakMode:
selettore in NSString
.
La dichiarazione (dalla documentazione di Apple) è:
- (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode
Questa API presenta le caratteristiche seguenti:
- Il tipo restituito è
CGSize
per l'API unificata. - Il
font
parametro è un oggetto UIFont (e un tipo (indirettamente) derivato da NSObject ed è mappato a System.IntPtr. - Il
width
parametro , unCGFloat
oggetto , viene mappato anfloat
. - Il
lineBreakMode
parametro , unUILineBreakMode
, è già stato associato in Xamarin.iOS comeUILineBreakMode
Enumerazione.
Mettendo tutto insieme, la objc_msgSend
dichiarazione deve corrispondere:
CGSize objc_msgSend(
IntPtr target,
IntPtr selector,
IntPtr font,
nfloat width,
UILineBreakMode mode
);
Dichiararlo come segue:
[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
);
Per chiamare questo metodo, usare 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 quella precedente SizeF
usata prima di passare alle API unificate), il codice precedente sarebbe stato eseguito nel simulatore, ma si è verificato un arresto anomalo del dispositivo. Per chiamare un selettore che restituisce un valore inferiore a 8 bit di dimensioni, dichiarare la objc_msgSend_stret
funzione:
[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
);
Per chiamare questo metodo, usare 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
);
Chiamata di un selettore
La chiamata di un selettore prevede tre passaggi:
- Ottenere la destinazione del selettore.
- Ottenere il nome del selettore.
- Chiamare
objc_msgSend
con gli argomenti appropriati.
Destinazioni del selettore
Una destinazione del selettore è un'istanza dell'oggetto o una Objective-C classe. Se la destinazione è un'istanza e proviene da un tipo Xamarin.iOS associato, usare la ObjCRuntime.INativeObject.Handle
proprietà .
Se la destinazione è una classe, usare ObjCRuntime.Class
per ottenere un riferimento all'istanza della classe , quindi usare la Class.Handle
proprietà .
Nomi del selettore
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.
Dopo aver ottenuto un nome del selettore, è possibile crearne un'istanza ObjCRuntime.Selector
.
Chiamata di objc_msgSend
objc_msgSend
invia un messaggio (selettore) a un oggetto . Questa famiglia di funzioni accetta almeno due argomenti obbligatori: la destinazione del selettore (un'istanza o un handle di classe), il selettore stesso e tutti gli argomenti necessari per il selettore. Gli argomenti dell'istanza e del selettore devono essere System.IntPtr
e tutti gli argomenti rimanenti devono corrispondere al tipo previsto dal selettore, ad esempio per nint
un int
oggetto o per System.IntPtr
tutti i NSObject
tipi derivati da . Usare il NSObject.Handle
per ottenere un oggetto per un'istanza IntPtr
Objective-C del tipo.
È presente più di una objc_msgSend
funzione:
- Usare
objc_msgSend_stret
per i selettori che restituiscono uno struct. In ARM sono inclusi tutti i tipi restituiti che non sono un'enumerazione o uno qualsiasi dei tipi predefiniti C (char
,short
,long
int
,float
, , ).double
In x86 (simulatore), questo metodo deve essere usato per tutte le strutture di dimensioni superiori a 8 byte (CGSize
è di 8 byte e non viene usatoobjc_msgSend_stret
nel simulatore). - Usare
objc_msgSend_fpret
per i selettori che restituiscono un valore a virgola mobile solo su x86. Questa funzione non deve essere usata in ARM; Usare inveceobjc_msgSend
. - La funzione objc_msgSend principale viene usata per tutti gli altri selettori.
Dopo aver deciso quali objc_msgSend
funzioni è necessario chiamare (simulatore e dispositivo possono richiedere un metodo diverso), è possibile usare un metodo normale [DllImport]
per dichiarare la funzione per una chiamata successiva.
Un set di dichiarazioni predefinite objc_msgSend
è disponibile in ObjCRuntime.Messaging
.
Chiamate diverse nel simulatore e nel dispositivo
Come descritto in precedenza, Objective-C include tre tipi di objc_msgSend
metodi: uno per le chiamate regolari, uno per le chiamate che restituiscono valori a virgola mobile (solo x86) e uno per le chiamate che restituiscono valori di struct. Quest'ultimo include il suffisso _stret
in ObjCRuntime.Messaging
.
Se si richiama un metodo che restituisce determinati struct (regole descritte di seguito), è necessario richiamare il metodo con il valore restituito come primo parametro come out
valore:
// 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 usare il _stret_
metodo è diversa da x86 e ARM.
Se si desidera che le associazioni funzionino sia sul simulatore che sul dispositivo, aggiungere 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);
}
Utilizzo del metodo objc_msgSend_stret
Durante la compilazione per ARM, usare objc_msgSend_stret
per qualsiasi tipo di valore che non è un'enumerazione o uno qualsiasi dei tipi di base per un'enumerazione (int
, byte
, short
long
, double
). float
Quando si compila per x86, usare objc_msgSend_stret
per qualsiasi tipo valore che non è un'enumerazione o uno dei tipi di base per un'enumerazione (int
, byte
, short
long
, double
float
) e la cui dimensione nativa è maggiore di 8 byte.
Creazione di firme personalizzate
Il gist seguente può essere usato per creare firme personalizzate, se necessario.