iOS 和 Mac Catalyst 上的 Mono 解释器
编译适用于 iOS 或 Mac Catalyst 的 NET Multi-Platform App UI (.NET MAUI) 应用时,编译器会将应用代码转换为 Microsoft 中间语言 (MSIL)。 在模拟器中运行 iOS 应用或 Mac Catalyst 应用时,.NET 公共语言运行 (CLR) 会使用即时 (JIT) 编译器编译 MSIL。 运行时,MSIL 编译为本机代码,可在应用的正确体系结构上运行。
但是,Apple 设置的 iOS 存在安全限制,禁止在设备上执行动态生成的代码。 同样,在模拟器中基于 ARM64 体系结构运行的 iOS 应用和基于 ARM64 体系结构上运行的 Mac Catalyst 应用,都不允许执行动态生成的代码。 为满足此限制,iOS 和 Mac Catalyst 应用使用提前 (AOT) 编译器编译托管代码。 这会生成可部署到 Apple 设备上的本机 ISO 二进制文件,或本机 Mac Catalyst 二进制文件。
AOT 通过缩短启动时间和各种其他性能优化措施带来好处。 但是,它还会限制某些功能在应用中使用:
- 有限的泛型支持。 并非所有可能的泛型实例都能在编译时确定。 .NET MAUI 发布版本中遇到的许多 iOS 特定问题都因此限制造成。
- 不允许动态代码生成。 这意味着
System.Relection.Emit
不可用,不支持System.Runtime.Remoting
,也不允许使用 C# 动态类型的某些用途。
发生 AOT 限制时,将触发 System.ExecutionEngineException
,并显示“只在 AOT 模式下运行时试图对方法进行 JIT 编译”。
Mono 解释器在遵守平台限制的同时克服了这些限制。 它使你可以在运行时解释应用的某些部分,而 AOT 编译其余部分。 但是,在生产应用中使用解释器存在一些潜在缺点:
- 启用解释器后,应用的大小通常会显著缩小,但在某些情况下,应用的大小也会增加。
- 应用执行速度会变慢,因为解释后的代码比 AOT 编译后的代码运行速度更慢。 这种执行速度的降低可能是无法测量的,也可能是不可接受的,因此应执行性能测试。
- 崩溃报告中的本机堆栈跟踪会变得不那么有用,因为它们会包含解释器的泛型帧,其不会提及正在执行的代码。 但是,托管堆栈跟踪不会更改。
.NET MAUI 调试生成时默认启用解释器,发布生成时也可启用解释器。
提示
如果 .NET MAUI iOS 应用或基于 ARM64 的 Mac Catalyst 应用作为调试版本正常运行,但发布生成时崩溃,请尝试为应用的发布生成启用解释器。 可能是你的应用或其一个库使用需要解释器的功能。
启用解释器
通过在 .NET MAUI 应用的项目文件中将 $(UseInterpreter)
MSBuild 属性设置为 true
,可以在 iOS 发布生成中启用 Mono 解释器:
<PropertyGroup Condition="$(TargetFramework.Contains('-ios')) and '$(Configuration)' == 'Release'">
<UseInterpreter>true</UseInterpreter>
</PropertyGroup>
还可以为 ARM64 上的 Mac Catalyst 版本生成启用解释器:
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'maccatalyst-arm64' and '$(Configuration)' == 'Release'">
<UseInterpreter>true</UseInterpreter>
</PropertyGroup>
警告
不要在 Android 上的发布生成时启用解释器,因为它会禁用 JIT 编译。
在 iOS 和 Mac Catalyst 上,还可以使用 $(MtouchInterpreter)
MSBuild 属性启用解释器。 此属性可以选择以逗号分隔的程序集列表进行解释。 此外,all
可用于指定所有程序集,如果前缀为减号,则程序集将被 AOT 编译。 这样,便可以:
- 通过指定
all
来解释所有程序集,或通过指定-all
来编译所有 AOT 程序集。 - 通过指定 MyAssembly 来解释单个程序集,或者通过指定 -MyAssembly 对单个程序集进行 AOT 编译。
- 混合和匹配解释某些程序集,AOT 编译其他程序集。
警告
解释器与本机 AOT 部署不兼容,因此 $(UseInterpreter)
,使用本机 AOT 时,和 $(MtouchInterpreter)
MSBuild 属性不起作用。 有关详细信息,请参阅本机 AOT 部署。
下列示例演示如何解释除“System.Xml.dll”之外的所有程序集:
<PropertyGroup Condition="$(TargetFramework.Contains('-ios')) and '$(Configuration)' == 'Release'">
<!-- Interpret everything, except System.Xml.dll -->
<MtouchInterpreter>all,-System.Xml</MtouchInterpreter>
</PropertyGroup>
下列示例演示如何使用 AOT 编译除“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>
重要说明
解释器执行的堆栈帧不会提供有用的信息。 但是,由于解释器可以按程序集禁用,因此可能在崩溃报告中准确描述某些程序集中的堆栈帧。
或者,使用下列示例对所有程序集进行 AOT 编译,同时仍允许解释器执行动态代码生成:
<PropertyGroup Condition="$(TargetFramework.Contains('-ios')) and '$(Configuration)' == 'Release'">
<MtouchInterpreter>-all</MtouchInterpreter>
</PropertyGroup>
有时需要解释器的另一种常见情况是,在 ARM64 体系结构上运行的 .NET MAUI Mac Catalyst 应用在启动时可能会出现异常。 启用解释器通常可以修复此启动异常:
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'maccatalyst-arm64' and '$(Configuration)' == 'Release'">
<MtouchInterpreter>-all,MyAssembly</MtouchInterpreter>
</PropertyGroup>