Type registrar pour Xamarin.iOS

Ce document décrit le système d’inscription de type utilisé par Xamarin.iOS.

Inscription de classes et de méthodes managées

Au démarrage, Xamarin.iOS s’inscrit :

  • Classes avec un attribut [Register] en tant que Objective-C classes.
  • Classes avec un attribut [Category] en tant que Objective-C catégories.
  • Interfaces avec un attribut [Protocole] en tant que Objective-C protocoles.
  • Membres disposant d’une [Exportation], ce qui permet Objective-C d’y accéder.

Par exemple, considérez la méthode managée Main courante dans les applications Xamarin.iOS :

UIApplication.Main (args, null, "AppDelegate");

Ce code indique au Objective-C runtime d’utiliser le type appelé AppDelegate comme classe déléguée de l’application. Pour que le Objective-C runtime puisse créer une instance de la classe C#AppDelegate, cette classe doit être inscrite.

Xamarin.iOS effectue l’inscription automatiquement, soit au moment de l’exécution (inscription dynamique) soit au moment de la compilation (inscription statique).

L’inscription dynamique utilise la réflexion au démarrage pour rechercher toutes les classes et méthodes à inscrire, en les transmettant au Objective-C runtime. L’inscription dynamique est utilisée par défaut pour les builds de simulateur.

L’inscription statique inspecte, au moment de la compilation, les assemblys utilisés par l’application. Il détermine les classes et les méthodes avec Objective-C lesquelles s’inscrire et génère une carte, qui est incorporée dans votre fichier binaire. Ensuite, au démarrage, il inscrit la carte avec le Objective-C runtime. L’inscription statique est utilisée pour les builds d’appareils.

Catégories

À partir de Xamarin.iOS 8.10, il est possible de créer Objective-C des catégories à l’aide de la syntaxe C#.

Pour créer une catégorie, utilisez l’attribut [Category] et spécifiez le type à étendre. Par exemple, le code suivant étend NSString:

[Category (typeof (NSString))]

Chacune des méthodes d’une catégorie a un [Export] attribut, ce qui le rend disponible pour le Objective-C runtime :

[Export ("today")]
public static string Today ()
{
    return "Today";
}

Toutes les méthodes d’extension managées doivent être statiques, mais il est possible de créer Objective-C des méthodes instance à l’aide de la syntaxe C# standard pour les méthodes d’extension :

[Export ("toUpper")]
public static string ToUpper (this NSString self)
{
    return self.ToString ().ToUpper ();
}

Le premier argument de la méthode d’extension est le instance sur lequel la méthode a été appelée :

[Category (typeof (NSString))]
public static class MyStringCategory
{
    [Export ("toUpper")]
    static string ToUpper (this NSString self)
    {
        return self.ToString ().ToUpper ();
    }
 }

Cet exemple ajoute une méthode instance native toUpper à la NSString classe . Cette méthode peut être appelée à partir de Objective-C:

[Category (typeof (UIViewController))]
public static class MyViewControllerCategory
{
    [Export ("shouldAutoRotate")]
    static bool GlobalRotate ()
    {
        return true;
    }
}

Protocoles

À compter de Xamarin.iOS 8.10, les interfaces avec l’attribut [Protocol] seront exportées vers Objective-C en tant que protocoles :

[Protocol ("MyProtocol")]
interface IMyProtocol
{
    [Export ("method")]
    void Method ();
}

class MyClass : IMyProtocol
{
    void Method ()
    {
    }
}

Ce code s’exporte IMyProtocol vers Objective-C un protocole appelé MyProtocol et une classe appelée MyClass qui implémente le protocole.

Nouveau système d’inscription

À partir de la version stable 6.2.6 et de la version bêta 6.3.4, nous avons ajouté une nouvelle version statique registrar. Dans la version 7.2.1, nous avons fait du nouveau la valeur registrar par défaut.

Ce nouveau système d’inscription offre les nouvelles fonctionnalités suivantes :

  • Détection au moment de la compilation des erreurs du programmeur :

    • Deux classes inscrites avec le même nom.
    • Plusieurs méthodes exportées pour répondre au même sélecteur
  • Suppression du code natif inutilisé :

    • Le nouveau système d’inscription ajoutera des références fortes au code utilisé dans les bibliothèques statiques, ce qui permettra à l’éditeur de liens natif de supprimer le code natif inutilisé du binaire résultant. Sur les exemples de liaisons de Xamarin, la plupart des applications deviennent au moins 300 000 plus petites.
  • Prise en charge des sous-classes génériques de NSObject; consultez NSObject Generics pour plus d’informations. En outre, le nouveau système d’inscription intercepte les constructions génériques non prises en charge qui auraient précédemment provoqué un comportement aléatoire au moment de l’exécution.

Erreurs interceptées par le nouveau registrar

Vous trouverez ci-dessous quelques exemples d’erreurs interceptées par le nouveau registrar.

  • Exportation du même sélecteur plusieurs fois dans la même classe :

    [Register]
    class MyDemo : NSObject
    {
        [Export ("foo:")]
        void Foo (NSString str);
        [Export ("foo:")]
        void Foo (string str)
    }
    
  • Exportation de plusieurs classes managées portant le même Objective-C nom :

    [Register ("Class")]
    class MyClass : NSObject {}
    
    [Register ("Class")]
    class YourClass : NSObject {}
    
  • Exportation de méthodes génériques :

    [Register]
    class MyDemo : NSObject
    {
        [Export ("foo")]
        void Foo<T> () {}
    }
    

Limitations du nouveau registrar

Voici quelques éléments à garder à l’esprit concernant le nouveau registrar:

  • Certaines bibliothèques tierces doivent être mises à jour pour fonctionner avec le nouveau système d’inscription. Pour plus d’informations, consultez les modifications requises ci-dessous.

  • Un inconvénient à court terme est également que Clang doit être utilisé si l’infrastructure des comptes est utilisée (en effet, l’en-tête accounts.h d’Apple ne peut être compilé que par Clang). Ajoutez --compiler:clang aux arguments mtouch supplémentaires pour utiliser Clang si vous utilisez Xcode 4.6 ou version antérieure (Xamarin.iOS sélectionne automatiquement Clang dans Xcode 5.0 ou version ultérieure.)

  • Si Xcode 4.6 (ou version antérieure) est utilisé, GCC/G++ doit être sélectionné si les noms de types exportés contiennent des caractères non ASCII (en effet, la version de Clang fournie avec Xcode 4.6 ne prend pas en charge les caractères non ASCII à l’intérieur des identificateurs dans le Objective-C code). Ajoutez --compiler:gcc aux arguments mtouch supplémentaires pour utiliser GCC.

Sélection d’un registrar

Vous pouvez sélectionner un autre registrar en ajoutant l’une des options suivantes aux arguments mtouch supplémentaires dans les paramètres de build iOS du projet :

  • --registrar:static – par défaut pour les builds d’appareils
  • --registrar:dynamic – par défaut pour les builds de simulateur

Notes

L’API Classic de Xamarin prenait en charge d’autres options telles que --registrar:legacystatic et --registrar:legacydynamic. Toutefois, ces options ne sont pas prises en charge par l’API unifiée.

Lacunes de l’ancien système d’inscription

L’ancien système d’inscription présente les inconvénients suivants :

  • Il n’y avait aucune référence statique (native) aux classes et méthodes Objective-C dans les bibliothèques natives tierces, ce qui signifie que nous ne pouvions pas demander à l’éditeur de liens natif de supprimer le code natif tiers qui n’était pas réellement utilisé (car tout serait supprimé). C’est la raison pour -force_load libNative.a laquelle chaque liaison tierce devait faire (ou l’équivalent ForceLoad=true dans l’attribut [LinkWith] ).
  • Vous pouvez exporter deux types managés portant le même Objective-C nom sans avertissement. Un scénario rare était de se retrouver avec deux AppDelegate classes dans des espaces de noms différents. Au moment de l’exécution, il serait complètement aléatoire lequel a été choisi (en fait, il variait entre les exécutions d’une application qui n’a même pas été reconstruite , ce qui a rendu une expérience de débogage très déroutante et frustrante).
  • Vous pouvez exporter deux méthodes avec la même Objective-C signature. Encore une fois, l’un d’entre Objective-C eux était aléatoire (mais ce problème n’était pas aussi courant que le précédent, principalement parce que la seule façon d’expérimenter ce bogue était de remplacer la méthode managée malchanceux).
  • L’ensemble de méthodes qui a été exporté était légèrement différent entre les builds dynamiques et statiques.
  • Il ne fonctionne pas correctement lors de l’exportation de classes génériques (dont l’implémentation générique exacte exécutée au moment de l’exécution serait aléatoire, ce qui entraînerait un comportement indéterminé).

Nouveau registrar: modifications obligatoires apportées aux liaisons

Cette section décrit les modifications de liaison qui doivent être effectuées pour fonctionner avec le nouveau registrar.

Les protocoles doivent avoir l’attribut [Protocole]

Les protocoles doivent maintenant avoir l’attribut [Protocol] . Si vous ne le faites pas, vous obtiendrez une erreur d’éditeur de liens natif, telle que :

Undefined symbols for architecture i386: "_OBJC_CLASS_$_ProtocolName", referenced from: ...

Les sélecteurs doivent avoir un nombre valide de paramètres

Tous les sélecteurs doivent indiquer correctement le nombre de paramètres. Auparavant, ces erreurs étaient ignorées et pouvaient entraîner des problèmes d’exécution.

En bref, le nombre de points-virgules doit correspondre au nombre de paramètres :

  • Aucun paramètre : foo
  • Un paramètre : foo:
  • Deux paramètres : foo:parameterName2:

Les utilisations suivantes sont incorrectes :

// Invalid: export takes no arguments, but function expects one
[Export ("apply")]
void Apply (NSObject target);

// Invalid: exported as taking an argument, but the managed version does not have one:
[Export ("display:")]
void Display ();

Utiliser le paramètre IsVariadic dans Exporter

Les fonctions variadiciques doivent utiliser l’argument IsVariadic de l’attribut [Export] :

[Export ("variadicMethod:", IsVariadic = true)]
void VariadicMethod (NSObject first, IntPtr subsequent);

Il est impossible de lier des classes qui n’existent pas dans la bibliothèque native. Si une classe a été supprimée ou renommée dans la bibliothèque native, veillez à mettre à jour les liaisons pour qu’elles correspondent.