Interpréteur Mono sur iOS et Mac Catalyst

Lorsque vous compilez une application .NET Multi-Platform App UI (.NET MAUI) pour iOS ou Mac Catalyst, le compilateur transforme le code de votre application en Microsoft Intermediate Language (MSIL). Lorsque vous exécutez l’application iOS dans le simulateur ou l’application Mac Catalyst, le common language runtime (CLR) .NET compile le MSIL en utilisant un compilateur juste-à-temps (JIT). Au moment du runtime, le MSIL est compilé en code natif qui peut s’exécuter sur l’architecture appropriée pour votre application.

Toutefois, il existe une restriction de sécurité sur iOS, définie par Apple, qui interdit l’exécution de code généré dynamiquement sur des appareils. De la même façon, l’exécution du code généré dynamiquement est interdite dans les applications iOS s’exécutant sur l’architecture ARM64 dans le simulateur et sur les applications Mac Catalyst s’exécutant sur l’architecture ARM64. Pour respecter cette restriction, les applications iOS et Mac Catalyst utilisent un compilateur Ahead Of Time (AOT) pour compiler le code managé. Cette opération produit un binaire iOS natif pouvant être déployé sur des appareils Apple ou un binaire Mac Catalyst natif.

AOT offre des avantages via une réduction du temps de démarrage et diverses autres optimisations des performances. Toutefois, il limite également l’utilisation de certaines fonctionnalités dans votre application :

  • La prise en charge des génériques est limitée. Les instanciations génériques possible ne peuvent pas toutes être déterminées lors de la compilation. Plusieurs des problèmes spécifiques à iOS rencontrés dans les builds de version .NET MAUI sont attribuables à cette limitation.
  • La génération de code dynamique n’est pas autorisée. Cela signifie que System.Relection.Emit n’est pas disponible, qu’il n’existe aucune prise en charge pour System.Runtime.Remoting et que certaines utilisations du type dynamique de C# ne sont pas permises.

Lorsqu'une limitation d'AOT se produit, une System.ExecutionEngineException est produite avec le message « Tentative de compilation JIT d'une méthode pendant l'exécution en mode AOT uniquement ».

L’interpréteur Mono surmonte ces limitations tout en se conformant aux limitations de la plateforme. Il vous permet d’interpréter certains éléments de votre application au moment du runtime pendant la compilation du reste par AOT. Cependant, il existe des inconvénients éventuels de l’utilisation de l’interpréteur dans une application de production :

  • Bien que la taille de l’application se réduise considérablement quand l’interpréteur est activé, elle peut augmenter dans certains cas.
  • La vitesse d’exécution de l’application sera plus lente, car le code interprété s’exécute plus lentement que le code AOT compilé. Cette réduction de vitesse d’exécution peut varier de non mesurable à inacceptable et vous devez donc effectuer un test de performances.
  • Les traces de pile natives des rapports d’incidents deviennent moins utiles, car elles contiennent des cadres génériques de l’interpréteur qui ne mentionnent pas le code en cours d’exécution. Toutefois, les traces de pile managées ne changent pas.

L’interpréteur est activé par défaut pour les builds de débogage .NET MAUI et vous pouvez l’activer pour les builds de mise en production.

Conseil

Si votre application .NET MAUI iOS ou votre application Mac Catalyst basée sur ARM64 fonctionne correctement en tant que build de débogage, mais se bloque en tant que build de mise en production, essayez d’activer l’interpréteur pour la build de mise en production de votre application. Il se peut que votre application, ou l’une de ses bibliothèques, utilise une fonctionnalité qui nécessite l’interpréteur.

Activer l’interpréteur

Vous pouvez activer l’interpréteur Mono dans des builds de mise en production iOS en définissant la $(UseInterpreter)propriété MSBuild sur la valeur true dans le fichier projet de votre application .NET MAUI :

<PropertyGroup Condition="$(TargetFramework.Contains('-ios')) and '$(Configuration)' == 'Release'">
    <UseInterpreter>true</UseInterpreter>
</PropertyGroup>

Vous pouvez également activer l’interpréteur pour des builds de mise en production Mac Catalyst sur ARM64 :

<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'maccatalyst-arm64' and '$(Configuration)' == 'Release'">
    <UseInterpreter>true</UseInterpreter>
</PropertyGroup>

Avertissement

N’activez pas l’interpréteur pour des builds de mise en production sur Android, car il désactive la compilation JIT.

Sur iOS et Mac Catalyst, vous pouvez également activer l’interpréteur avec la propriété MSBuild $(MtouchInterpreter). Cette propriété peut éventuellement prendre une liste d’assemblies séparée par des virgules à interpréter. En outre, all peut être utilisé pour spécifier tous les assemblages, et lorsqu'ils sont précédés d'un signe moins, un assemblage sera compilé en AOT. Vous pouvez ainsi effectuer les opérations suivantes :

  • Interprétez tous les assemblages en spécifiant all ou compilez AOT tout en spécifiant -all.
  • Interprétez des assemblies individuels en spécifiant MyAssembly ou compilez en AOT des assemblies individuels en spécifiant -MyAssembly.
  • Combinez et interprétez certaines assemblies et compilez en AOT d'autres assemblies.

Avertissement

L'interpréteur n'est pas compatible avec le déploiement AOT natif, et par conséquent, les propriétés $(UseInterpreter) et $(MtouchInterpreter) MSBuild n'ont aucun effet lors de l'utilisation du déploiement AOT natif. Pour plus d’informations, consultez Déploiement AOT natif.

L’exemple suivant montre comment interpréter tous les assemblys à l’exception de System.Xml.dll :

<PropertyGroup Condition="$(TargetFramework.Contains('-ios')) and '$(Configuration)' == 'Release'">
    <!-- Interpret everything, except System.Xml.dll -->
    <MtouchInterpreter>all,-System.Xml</MtouchInterpreter>
</PropertyGroup>

L’exemple suivant montre comment compiler AOT tous les assemblys à l’exception de System.Numerics.dll :

<PropertyGroup Condition="$(TargetFramework.Contains('-ios')) and '$(Configuration)' == 'Release'">
    <!-- AOT everything, except System.Numerics.dll, which will be interpreted -->
    <MtouchInterpreter>-all,System.Numerics</MtouchInterpreter>
</PropertyGroup>

Important

Une trame de pile exécutée par l’interpréteur n'offre pas d'informations utiles. Puisque l'interpréteur peut être désactivé pour chaque assemblage, il est possible d'avoir des trames de pile de certains assemblages correctement représentées dans les rapports d'incident.

Vous pouvez également utiliser l’exemple suivant pour compiler AOT tous les assemblys tout en autorisant l’interpréteur à effectuer une génération de code dynamique :

<PropertyGroup Condition="$(TargetFramework.Contains('-ios')) and '$(Configuration)' == 'Release'">
    <MtouchInterpreter>-all</MtouchInterpreter>
</PropertyGroup>

L’autre scénario courant dans lequel l’interpréteur est parfois nécessaire correspond à une application .NET MAUI Mac Catalyst s’exécutant sur l’architecture ARM64 qui peut lever une exception au lancement. Vous pouvez souvent corriger cette exception de lancement en activant l’interpréteur :

<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'maccatalyst-arm64' and '$(Configuration)' == 'Release'">
    <MtouchInterpreter>-all,MyAssembly</MtouchInterpreter>
</PropertyGroup>