Fonctionnement de Xamarin.Mac

La plupart du temps, le développeur n’aura jamais à se soucier de la « magie » interne de Xamarin.Mac, cependant, avoir une compréhension approximative de la façon dont les choses fonctionnent sous le capot aidera à interpréter la documentation existante avec un objectif C# et à déboguer les problèmes lorsqu’ils se produisent.

Dans Xamarin.Mac, une application établit un pont entre deux mondes : il existe le Objective-C runtime basé contenant des instances de classes natives (NSString, , NSApplicationetc.) et le runtime C# contenant des instances de classes managées (System.String, HttpClient, etc.). Entre ces deux mondes, Xamarin.Mac crée un pont bidirectionnel afin qu’une application puisse appeler des méthodes (sélecteurs) dans Objective-C (par NSApplication.Initexemple ) et Objective-C puisse rappeler les méthodes C# de l’application (comme les méthodes sur un délégué d’application). En général, les appels à Objective-C sont gérés de manière transparente via P/Invokes et un code d’exécution fourni par Xamarin.

Exposition des classes/méthodes C# à Objective-C

Toutefois, pour Objective-C rappeler les objets C# d’une application, ils doivent être exposés d’une manière compréhensible Objective-C . Cette opération s’effectue via les Register attributs et Export . Prenons l’exemple suivant :

[Register ("MyClass")]
public class MyClass : NSObject
{
   [Export ("init")]
   public MyClass ()
   {
   }

   [Export ("run")]
   public void Run ()
   {
   }
}

Dans cet exemple, le Objective-C runtime connaît désormais une classe appelée MyClass avec des sélecteurs appelés init et run.

Dans la plupart des cas, il s’agit d’un détail d’implémentation que le développeur peut ignorer, car la plupart des rappels reçus par une application se feront via des méthodes remplacées sur base des classes (telles que AppDelegate, Delegates, ) DataSourcesou sur des actions passées dans les API. Dans tous ces cas, Export les attributs ne sont pas nécessaires dans le code C#.

Exécution du constructeur

Dans de nombreux cas, le développeur doit exposer l’API de construction des classes C# de l’application Objective-C au runtime afin qu’elle puisse être instanciée à partir d’emplacements tels que lorsqu’elle est appelée dans des fichiers Storyboard ou XIB. Voici les cinq constructeurs les plus courants utilisés dans les applications Xamarin.Mac :

// Called when created from unmanaged code
public CustomView (IntPtr handle) : base (handle)
{
   Initialize ();
}

// Called when created directly from a XIB file
[Export ("initWithCoder:")]
public CustomView (NSCoder coder) : base (coder)
{
   Initialize ();
}

// Called from C# to instance NSView with a Frame (initWithFrame)
public CustomView (CGRect frame) : base (frame)
{
}

// Called from C# to instance NSView without setting the frame (init)
public CustomView () : base ()
{
}

// This is a special case constructor that you call on a derived class when the derived called has an [Export] constructor.
// For example, if you call init on NSString then you don’t want to call init on NSObject.
public CustomView () : base (NSObjectFlag.Empty)
{
}

En général, le développeur doit laisser les constructeurs et NSCoder qui sont générés lors de la IntPtr création de certains types, tels que personnalisés NSViews seuls. Si Xamarin.Mac doit appeler l’un de ces constructeurs en réponse à une demande d’exécution Objective-C et que vous l’avez supprimée, l’application se bloque dans le code natif et il peut être difficile de comprendre exactement le problème.

Gestion de la mémoire et cycles

La gestion de la mémoire dans Xamarin.Mac est à bien des égards très similaire à Xamarin.iOS. Il s’agit également d’un sujet complexe, qui dépasse le cadre de ce document. Lisez les meilleures pratiques en matière de mémoire et de performances.

Compilation à l’avance

En règle générale, les applications .NET ne sont pas compilées en code machine lorsqu’elles sont générées, mais en une couche intermédiaire appelée code IL qui est compilée juste-à-temps (JIT) dans le code de l’ordinateur lors du lancement de l’application.

Le temps nécessaire au runtime mono pour compiler ce code d’ordinateur peut ralentir le lancement d’une application Xamarin.Mac jusqu’à 20 %, car la génération du code machine nécessaire prend du temps.

En raison des limitations imposées par Apple sur iOS, la compilation JIT du code IL n’est pas disponible pour Xamarin.iOS. Par conséquent, toutes les applications Xamarin.iOS sont complètes avant-temps (AOT) compilées en code machine pendant le cycle de génération.

La nouveauté de Xamarin.Mac est la possibilité d’AOT le code IL pendant le cycle de génération de l’application, tout comme Xamarin.iOS peut le faire. Xamarin.Mac utilise une approche AOT hybride qui compile la majorité du code de machine nécessaire, mais permet au runtime de compiler les trampolines nécessaires et la flexibilité de continuer à prendre en charge Reflection.Emit (et d’autres cas d’usage qui fonctionnent actuellement sur Xamarin.Mac).

AOT peut aider une application Xamarin.Mac dans deux domaines principaux :

  • Meilleurs journaux d’incident « natifs » : si une application Xamarin.Mac se bloque dans le code natif, ce qui est courant lors d’appels non valides dans les API Cocoa (par exemple, en envoyant un null dans une méthode qui ne l’accepte pas), les journaux d’incident natifs avec des trames JIT sont difficiles à analyser. Étant donné que les trames JIT n’ont pas d’informations de débogage, il y aura plusieurs lignes avec des décalages hexadécimaux et aucune idée de ce qui se passait. AOT génère des trames nommées « réelles » et les traces sont beaucoup plus faciles à lire. Cela signifie également que l’application Xamarin.Mac interagira mieux avec les outils natifs tels que lldb et Instruments.
  • Meilleures performances au moment du lancement : pour les applications Xamarin.Mac volumineuses, avec un temps de démarrage de plusieurs secondes, la compilation JIT de tout le code peut prendre beaucoup de temps. AOT effectue ce travail à l’avance.

Activation de la compilation AOT

AOT est activé dans Xamarin.Mac en double-cliquant sur le nom du projet dans le Explorateur de solutions, en accédant à Mac Build et en ajoutant --aot:[options] au champ Arguments mmp supplémentaires : (où [options] est une ou plusieurs options pour contrôler le type AOT, voir ci-dessous). Par exemple :

Ajout d’AOT à des arguments mmp supplémentaires

Important

L’activation de la compilation AOT augmente considérablement le temps de génération, parfois jusqu’à plusieurs minutes, mais elle peut améliorer les temps de lancement de l’application de 20 % en moyenne. Par conséquent, la compilation AOT ne doit être activée que sur les builds Release d’une application Xamarin.Mac.

Options de compilation Aot

Il existe plusieurs options différentes qui peuvent être ajustées lors de l’activation de la compilation AOT sur une application Xamarin.Mac :

  • none - Aucune compilation AOT. Il s'agit du paramètre par défaut.
  • all - AOT compile chaque assembly dans le MonoBundle.
  • core- AOT compile les Xamarin.Macassemblys et mscorlibSystem .
  • sdk - AOT compile les Xamarin.Mac assemblys BCL (Bibliothèques de classes de base) et .
  • |hybrid - L’ajout de ceci à l’une des options ci-dessus active l’AOT hybride, ce qui permet le stripping il, mais entraîne des temps de compilation plus longs.
  • + - Inclut un seul fichier pour la compilation AOT.
  • - - Supprime un seul fichier de la compilation AOT.

Par exemple, --aot:all,-MyAssembly.dll activerait la compilation AOT sur tous les assemblys dans le MonoBundle à l’exceptionMyAssembly.dll de et --aot:core|hybrid,+MyOtherAssembly.dll,-mscorlib.dll activerait hybride, le code AOT inclut et MyOtherAssembly.dll exclut le mscorlib.dll.

Statique partielle registrar

Lors du développement d’une application Xamarin.Mac, réduire le temps entre la réalisation d’une modification et le test peut devenir important pour respecter les échéances de développement. Des stratégies telles que la modularisation des codebases et des tests unitaires peuvent aider à réduire les temps de compilation, car elles réduisent le nombre de fois où une application nécessite une reconstruction complète coûteuse.

En outre, et en nouveauté de Xamarin.Mac, La statique Registrarpartielle (telle que lancée par Xamarin.iOS) peut réduire considérablement les temps de lancement d’une application Xamarin.Mac dans la configuration de débogage. Comprendre comment l’utilisation de la statique Registrar partielle peut entraîner une amélioration presque de 5 fois dans le lancement de débogage prendra un peu d’arrière-plan sur ce que registrar est, quelle est la différence entre statique et dynamique, et ce que fait cette version « statique partielle ».

À propos du registrar

Sous le capot d’une application Xamarin.Mac se trouve l’infrastructure Cocoa d’Apple et le Objective-C runtime. La création d’un pont entre ce « monde natif » et le « monde managé » de C# est la responsabilité principale de Xamarin.Mac. Une partie de cette tâche est gérée par le registrar, qui est exécuté à l’intérieur de la NSApplication.Init () méthode . C’est l’une des raisons pour lesquelles toute utilisation des API Cocoa dans Xamarin.Mac nécessite NSApplication.Init d’être appelée en premier.

Le registrartravail consiste à informer le Objective-C runtime de l’existence des classes C# de l’application qui dérivent de classes telles que NSApplicationDelegate, NSView, NSWindowet NSObject. Cela nécessite une analyse de tous les types dans l’application pour déterminer ce qui doit être inscrit et les éléments sur chaque type à signaler.

Cette analyse peut être effectuée de manière dynamique, au démarrage de l’application avec réflexion, ou de manière statique, en tant qu’étape de génération. Lors du choix d’un type d’inscription, le développeur doit connaître les éléments suivants :

  • L’inscription statique peut réduire considérablement les temps de lancement, mais peut ralentir considérablement les temps de génération (généralement plus que le double temps de génération de débogage). Il s’agit de la valeur par défaut pour les builds de configuration release .
  • L’inscription dynamique retarde ce travail jusqu’au lancement de l’application et ignore la génération de code, mais ce travail supplémentaire peut créer une pause notable (au moins deux secondes) au lancement de l’application. Cela se remarque particulièrement dans les builds de configuration de débogage, qui sont par défaut l’inscription dynamique et dont la réflexion est plus lente.

L’inscription statique partielle, introduite pour la première fois dans Xamarin.iOS 8.13, offre au développeur le meilleur des deux options. En précalquant les informations d’inscription de chaque élément dans et en Xamarin.Mac.dll expédiant ces informations avec Xamarin.Mac dans une bibliothèque statique (qui doit uniquement être liée au moment de la génération), Microsoft a supprimé la majeure partie du temps de réflexion de la dynamique registrar tout en n’affectant pas le temps de génération.

Activation de la statique partielle registrar

La statique Registrar partielle est activée dans Xamarin.Mac en double-cliquant sur le nom du projet dans le Explorateur de solutions, en accédant à Build Mac et en ajoutant --registrar:static au champ Arguments mmp supplémentaires : . Par exemple :

Ajout de la statique registrar partielle à des arguments mmp supplémentaires

Ressources supplémentaires

Voici quelques explications plus détaillées sur la façon dont les choses fonctionnent en interne :