Objective-C sélecteurs dans Xamarin.iOS
La Objective-C langue est basée sur des sélecteurs. Un sélecteur est un message qui peut être envoyé à un objet ou à une classe. Xamarin.iOS mappe instance sélecteurs aux méthodes instance, et les sélecteurs de classe aux méthodes statiques.
Contrairement aux fonctions C normales (et comme les fonctions membres C++), vous ne pouvez pas appeler directement un sélecteur à l’aide de P/Invoke À la place, les sélecteurs sont envoyés à une Objective-C classe ou instance à l’aide duobjc_msgSend
Fonction.
Pour plus d’informations sur les messages dans Objective-C, consultez le guide Utilisation des objets d’Apple.
Exemple
Supposons que vous souhaitiez appeler lesizeWithFont:forWidth:lineBreakMode:
sélecteur sur NSString
.
La déclaration (issue de la documentation d’Apple) est la suivante :
- (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode
Cette API présente les caractéristiques suivantes :
- Le type de retour est
CGSize
pour l’API unifiée. - Le
font
paramètre est un UIFont (et un type (indirectement) dérivé de NSObject, et est mappé à System.IntPtr. - Le
width
paramètre ,CGFloat
, est mappé ànfloat
. - Le
lineBreakMode
paramètre ,UILineBreakMode
, a déjà été lié dans Xamarin.iOS en tant queUILineBreakMode
Énumération.
En mettant tout cela ensemble, la objc_msgSend
déclaration doit correspondre à :
CGSize objc_msgSend(
IntPtr target,
IntPtr selector,
IntPtr font,
nfloat width,
UILineBreakMode mode
);
Déclarez-le comme suit :
[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
);
Pour appeler cette méthode, utilisez le code suivant :
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
);
Si la valeur retournée était une structure dont la taille était inférieure à 8 octets (comme l’ancienne SizeF
utilisée avant de basculer vers les API unifiées), le code ci-dessus aurait été exécuté sur le simulateur, mais s’est bloqué sur l’appareil. Pour appeler un sélecteur qui retourne une valeur inférieure à 8 bits, déclarez la objc_msgSend_stret
fonction :
[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
);
Pour appeler cette méthode, utilisez le code suivant :
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
);
Appel d’un sélecteur
L’appel d’un sélecteur se fait en trois étapes :
- Obtenez la cible du sélecteur.
- Obtenez le nom du sélecteur.
- Appelez
objc_msgSend
avec les arguments appropriés.
Cibles de sélecteur
Une cible de sélecteur est un objet instance ou une Objective-C classe. Si la cible est un instance et provient d’un type Xamarin.iOS lié, utilisez la ObjCRuntime.INativeObject.Handle
propriété .
Si la cible est une classe, utilisez ObjCRuntime.Class
pour obtenir une référence à la classe instance, puis utilisez la Class.Handle
propriété .
Noms des sélecteurs
Les noms des sélecteurs sont répertoriés dans la documentation d’Apple. Par exemple, NSString
inclut sizeWithFont:
les sélecteurs et sizeWithFont:forWidth:lineBreakMode:
. Les deux-points incorporés et de fin font partie du nom du sélecteur et ne peuvent pas être omis.
Une fois que vous avez un nom de sélecteur, vous pouvez créer un ObjCRuntime.Selector
instance pour celui-ci.
Appel de objc_msgSend
objc_msgSend
envoie un message (sélecteur) à un objet . Cette famille de fonctions prend au moins deux arguments requis : la cible du sélecteur (un instance ou un handle de classe), le sélecteur lui-même et tous les arguments requis pour le sélecteur. Les arguments instance et sélecteur doivent être System.IntPtr
, et tous les arguments restants doivent correspondre au type attendu par le sélecteur, par exemple un nint
pour un int
ou un System.IntPtr
pour tous les NSObject
types dérivés. Utilisez leNSObject.Handle
pour obtenir un IntPtr
pour un Objective-C type instance.
Il existe plusieurs objc_msgSend
fonctions :
- Utilisez
objc_msgSend_stret
pour les sélecteurs qui retournent un struct. Sur ARM, cela inclut tous les types de retour qui ne sont pas une énumération ou l’un des types intégrés C (char
,short
,int
,long
,float
, ).double
Sur x86 (le simulateur), cette méthode doit être utilisée pour toutes les structures d’une taille supérieure à 8 octets (CGSize
est de 8 octets et n’est pas utiliséeobjc_msgSend_stret
dans le simulateur). - Utilisez
objc_msgSend_fpret
pour les sélecteurs qui retournent une valeur à virgule flottante sur x86 uniquement. Cette fonction n’a pas besoin d’être utilisée sur ARM ; à la place, utilisezobjc_msgSend
. - La fonction objc_msgSend main est utilisée pour tous les autres sélecteurs.
Une fois que vous avez décidé objc_msgSend
quelle(s) fonction(s) vous devez appeler (le simulateur et l’appareil peuvent chacun nécessiter une méthode différente), vous pouvez utiliser une méthode normale [DllImport]
pour déclarer la fonction pour un appel ultérieur.
Vous trouverez un ensemble de déclarations prédéfinis objc_msgSend
dans ObjCRuntime.Messaging
.
Différents appels sur le simulateur et l’appareil
Comme décrit ci-dessus, Objective-C a trois types de objc_msgSend
méthodes : une pour les appels réguliers, une pour les appels qui retournent des valeurs à virgule flottante (x86 uniquement) et une pour les appels qui retournent des valeurs de struct. Ce dernier inclut le suffixe _stret
dans ObjCRuntime.Messaging
.
Si vous appelez une méthode qui retourne certains structs (règles décrites ci-dessous), vous devez appeler la méthode avec la valeur de retour comme premier paramètre comme out
valeur :
// 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 règle d’utilisation de la _stret_
méthode diffère sur x86 et ARM.
Si vous souhaitez que vos liaisons fonctionnent à la fois sur le simulateur et sur l’appareil, ajoutez du code comme suit :
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);
}
Utilisation de la méthode objc_msgSend_stret
Lors de la génération pour ARM, utilisez leobjc_msgSend_stret
pour tout type valeur qui n’est pas une énumération ou l’un des types de base d’une énumération (int
, byte
, short
, long
, double
, float
).
Lors de la génération pour x86, utilisezobjc_msgSend_stret
pour tout type valeur qui n’est pas une énumération ou l’un des types de base d’une énumération (int
, byte
, short
, long
double
, , float
) et dont la taille native est supérieure à 8 octets.
Création de vos propres signatures
Le gist suivant peut être utilisé pour créer vos propres signatures, si nécessaire.