Sélecteurs Objective-C dans Xamarin.iOS

  • 07/12/2017
  • 4 minutes à lire
    • d
    • d
    • c
    • a

Le langage Objective-C est basé sur des sélecteurs. Un sélecteur est un message qui peut être envoyé à un objet ou à une classe. Xamarin.iOS mappe les sélecteurs d’instance aux méthodes d’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 en utilisant P / Invoke À la place, les sélecteurs sont envoyés à une classe ou une instance Objective-C en utilisant la fonction objc_msgSend.

Pour plus d’informations sur les messages dans Objective-C, consultez le travail d’Apple avec Objectsguide.

Exemple

Supposons que vous souhaitiez appeler le sélecteur sizeWithFont:forWidth:lineBreakMode: sur NSString.La déclaration (de la documentation d’Apple) est:

- (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 paramètre font est un UIFont (et un type (indirectement) dérivé de NSObject, et est mappé au système.IntPtr.
  • Le paramètre width, a CGFloat, est mappé à nfloat.
  • Le paramètre lineBreakMode, a UILineBreakMode, a déjà été lié en Xamarin.iOS comme énumération UILineBreakMode.

En mettant tout cela ensemble, la déclaration objc_msgSend doit correspondre:

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

Déclarez-le comme suit:

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 un code tel que le 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 renvoyée avait été une structure de moins de 8 octets (comme l’ancienne SizeF utilisée avant de passer aux API unifiées), le code ci-dessus se serait exécuté sur le simulateur mais s’est écrasé sur le périphérique. Pour appeler un sélecteur qui renvoie une valeur de taille inférieure à 8 bits, déclarez la fonction 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 un code tel que le 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 );

Invoquer un sélecteur

L’appel d’un sélecteur comporte trois étapes:

  1. Obtenez la cible du sélecteur.
  2. Récupère le nom du sélecteur.
  3. Appelez objc_msgSend avec les arguments appropriés.

Cibles de sélection

Une cible de sélection est soit une instance d’objet, soit une classe Objective-C. Ifla cible est une instance et provient d’un Xamarin lié.Type iOS, utilisez la propriété ObjCRuntime.INativeObject.Handle.

Si la cible est une classe, utilisez ObjCRuntime.Class pour obtenir une référence à classinstance, puis utilisez la propriété Class.Handle.

Noms des sélecteurs

Les noms des sélecteurs sont répertoriés dans la documentation d’Apple. Par exemple, NSString inclut les sélecteurs sizeWithFont: et sizeWithFont:forWidth:lineBreakMode:. Les deux-points intégrés et les deux-points 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 une instance ObjCRuntime.Selector pour cela.

Appeler 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 (aninstance ou poignée de classe), le sélecteur lui-même et tout argument requis pour le sélecteur. Les arguments d’instance et de sélecteur doivent être System.IntPtr, et tous les arguments restants doivent correspondre au type attendu par theselector, par exemple un nint pour un int, ou un System.IntPtr pour tous les types dérivés NSObject. Utilisez la propriété NSObject.Handle pour obtenir un IntPtr pour une instance de type Objective-C.

Il y a plus d’une fonction objc_msgSend:

  • Utilisez objc_msgSend_stret pour les sélecteurs qui renvoient une structure. 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 de plus de 8 octets (CGSize est de 8 octets et n’utilise pas objc_msgSend_stret dans le simulateur).
  • Utilisez objc_msgSend_fpret pour les sélecteurs qui renvoient une valeur en virgule flottante sur x86 uniquement. Cette fonction n’a pas besoin d’être utilisée sur ARM ; utilisez plutôt objc_msgSend.
  • La fonction MAINOBJC_MSGSEND est utilisée pour tous les autres sélecteurs.

Une fois que vous avez décidé quelle (s) fonction (s) objc_msgSend vous devez appeler (le simulateur et le périphérique peuvent chacun nécessiter une méthode différente), vous pouvez utiliserune méthode normale pour déclarer la fonction pour une invocation ultérieure.

Un ensemble de déclarations pré-faites objc_msgSend se trouve dans ObjCRuntime.Messaging.

Invocations différentes sur le simulateur et le périphérique

Comme décrit ci-dessus, Objective-C dispose de trois types de méthodes objc_msgSend : une pour les invocations régulières, une pour les invocations qui renvoient des valeurs de point de chargement (x86 uniquement) et une pour les invocations qui renvoient des valeurs de structure. Ce dernier inclut le suffixe _stret dans ObjCRuntime.Messaging.

Si vous appelez une méthode qui retournera certaines structures (règles décrites ci-dessous), vous devez appeler la méthode avec la valeur de retour comme premier paramètre en tant que valeur 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 règle pour savoir quand utiliser la méthode _stret_ 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 tel que le suivant:

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 construction pour ARM, utilisez la objc_msgSend_stret pour tout type de valeur qui n’est pas une énumération ou l’un des types de base pour une énumération (int, byte, short, long, double, float).

Lors de la construction pour x86, utilisez objc_msgSend_stret pour tout type de valeur qui n’est pas une énumération ou l’un des types de base pour 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

L’essentiel suivant peut être utilisé pour créer vos propres signatures, si nécessaire.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.