通过


iOS 和 Mac Catalyst 上的本机 AOT 部署

提前式编译(AOT)部署在 iOS 和 Mac Catalyst 上生成一个 .NET 多平台应用 UI(.NET MAUI)应用,该应用程序已编译为本机代码。 本机 AOT 执行静态程序分析、完全裁剪应用,在删除未被静态引用的代码方面非常激进,并提前生成代码。

发布和部署本机 AOT 应用会产生以下优势:

  • 减小了应用包大小。
  • 启动时间更快。
  • 生成时间更快。

本机 AOT 将会对 .NET 运行时的某些功能施加限制,应该仅在应用程序大小和性能至关重要的场合使用。 这将要求你根据 Native AOT 的要求调整你的应用程序,这意味着确保它们完全裁剪并 AOT 兼容。 有关本机 AOT 限制的详细信息,请参阅 本机 AOT 限制

当启用本机 AOT 部署后,构建系统会分析您的代码及其所有依赖项,以验证它是否适合完整修剪和 AOT 编译。 如果检测到不兼容,则会生成剪裁和 AOT 警告。 单个裁剪或 AOT 系统警告意味着该应用程序与本机 AOT 部署不兼容,并可能无法正常运行。 因此,在构建用于本机提前编译 (AOT) 部署的应用程序时,您应查看并更正所有裁剪和 AOT 警告。 未能执行此操作可能会导致运行时出现异常,因为可能已删除必要的代码。 如果抑制警告,则必须对 AOT 部署的应用进行全面测试,以验证功能是否与未修剪的应用保持一致。 有关详细信息,请参阅 剪裁警告简介AOT警告简介

注意

在某些情况下,修复修整和 AOT 警告是不可能的,例如,当第三方库出现这些警告时。 在这种情况下,需要更新第三方库才能完全兼容。

本机 AOT 性能优势

发布和部署 Native AOT 应用程序通常会生成体积减小至最多 2.5 倍的小应用程序,且启动速度最多提高 2 倍。 但是,确切的性能优势取决于多种因素,包括正在使用的平台、运行应用的设备以及应用本身。

重要

以下图表显示了 dotnet new maui 应用在 iOS 和 Mac Catalyst 上的本机 AOT 部署的典型性能优势。 但是,确切的数据依赖于硬件,将来的版本可能会更改。

下图显示了不同部署模型中 iOS 和 Mac Catalyst 应用的应用包大小 dotnet new maui

显示不同部署模型中的应用包大小的图表。

上图显示,与默认部署模型相比,本机 AOT 通常为 iOS 和 Mac Catalyst 生成 2 倍以上的小型应用。

下图显示了在特定硬件上,iOS 和 Mac Catalyst 平台的应用在 Mono 和 Native AOT 部署下的平均启动时间:

显示 Mono 和 Native AOT 上平均应用启动时间的图表。

上图显示,与 Mono 部署相比,本机 AOT 在 iOS 设备上通常启动时间最多为 2 倍,在 Mac Catalyst 上启动时间快 1.2 倍。

下图显示了 iOS 和 Mac Catalyst 上应用在不同部署模型中的特定硬件 dotnet new maui 上的平均生成时间:

显示 Mono 和 Native AOT 上平均应用生成时间的图表。

上图显示,在 iOS 设备上,与默认部署模型相比,Native AOT 的构建速度通常最高可达 2.8 倍。 对于 Mac Catalyst,生成时间与 arm64 单一 RID 应用相当,但与 Mono 部署相比,通用应用的速度略慢。

重要

在许多场景中,Native AOT 将生成更小、更快的应用程序。 但是,在某些情况下,原生 AOT 可能不会生成更小、更快的应用程序。 因此,测试和分析应用以确定启用本机 AOT 部署的结果非常重要。

使用本机 AOT 发布

本机 AOT 部署模型通过$(PublishAot)生成属性和dotnet publish命令启用。 以下示例演示如何修改项目文件以在 iOS 和 Mac Catalyst 上启用本机 AOT 部署:

<PropertyGroup>
  <!-- enable trimming and AOT analyzers on all platforms -->
  <IsAotCompatible>true</IsAotCompatible>

  <!-- select platforms to use with NativeAOT -->
  <PublishAot Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">true</PublishAot>
  <PublishAot Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">true</PublishAot>
</PropertyGroup>

将所有平台的 $(IsAotCompatible) 生成属性设置为 true启用剪裁和 AOT 分析器。 这些分析器可帮助你识别与剪裁或 AOT 不兼容的代码。

对于 iOS 和 Mac Catalyst,在生成期间有条件地将 $(PublishAot) 设置为 true 可启用动态代码使用分析,并在发布期间进行本机 AOT 编译。 本机提前编译(AOT)分析包括应用的所有代码以及应用依赖的任何库。

警告

$(PublishAot)生成属性不应受到生成配置的限制。 这是因为依据构建属性 $(PublishAot) 的值启用或禁用精简特性开关,并且应在所有构建配置中启用或禁用相同的特性,以便代码的行为保持一致。 有关剪裁功能开关的详细信息,请参阅 剪裁功能开关

验证本机 AOT 应用是否正常工作的唯一方法是通过 dotnet publish 发布应用,然后验证代码及其依赖项是否没有剪裁或 AOT 警告。 具体而言, dotnet build -t:Publish 不等效于 dotnet publish

dotnet publish使用以下命令通过 Native AOT 部署, 在 iOS 和 Mac Catalyst 上发布您的应用:

# iOS
dotnet publish -f net9.0-ios -r ios-arm64

# Mac Catalyst
dotnet publish -f net9.0-maccatalyst -r maccatalyst-arm64
dotnet publish -f net9.0-maccatalyst -r maccatalyst-x64

# Universal Mac Catalyst apps
# (when <RuntimeIdentifiers>maccatalyst-x64;maccatalyst-arm64</RuntimeIdentifiers> is set in the project file)
dotnet publish -f net9.0-maccatalyst

提示

为了在开发生命周期的早期阶段能够及早发现剪裁或 AOT 问题,应频繁发布应用程序。

本机 AOT 限制

本机 AOT 将会对 .NET 运行时的某些功能施加限制,应该仅在应用程序大小和性能至关重要的场合使用。 它将要求你根据本机提前编译的要求来调整应用,这意味着确保它们完全裁剪且与提前编译兼容,这可能需要大量的工作。 除了本机 AOT 部署.NET 限制外,适用于 .NET MAUI 的本机 AOT 部署还有其他限制。

应用依赖的第三方库可能不兼容 AOT。 确保库剪裁且 AOT 兼容的唯一方法是使用本机 AOT 部署和 dotnet publish 命令发布应用,并查看本机 AOT 编译器是否为库生成任何警告。 有关使自己的库 AOT 兼容的信息,请参阅 如何使库与本机 AOT 兼容。

反射和动态代码

本机 AOT 部署限制在代码及其依赖项中使用反射,因此可能需要使用注释来帮助本机 AOT 编译器理解反射模式。 当编译器遇到无法静态分析的反射模式时,因此无法生成应用,则会生成剪裁警告。 本地 AOT 还禁止在应用中使用动态代码。 例如,编译 System.Linq.Expressions 无法按预期工作,并且无法在运行时加载和执行程序集。 当编译器遇到无法提前编译的动态模式时,它将生成 AOT 警告。

在 .NET MAUI 应用中,这意味着:

重要

Mono 解释器与本机 AOT 部署不兼容,因此在使用本机 AOT 时,$(UseInterpreter)$(MtouchInterpreter) MSBuild 属性不起作用。 有关 Mono 解释器的详细信息,请参阅 iOS 和 Mac Catalyst 上的 Mono 解释器

有关剪裁警告的详细信息,请参阅 剪裁警告简介。 有关 AOT 警告的详细信息,请参阅 AOT 警告简介。

将应用调整为本机 AOT 部署

使用以下清单帮助你将应用程序符合本机 AOT 部署要求:

  • 确保所有 XAML 都已被编译:
    • 删除所有 [XamlCompilation(XamlCompilationOptions.Skip)] 用法。
    • 删除所有 <?xaml-comp compile="false" ?> 用法。
  • 删除对 LoadFromXaml 该方法的所有调用。
  • 确保编译所有数据绑定。 有关详细信息,请参阅已编译的绑定
    • 确保所有 XAML 数据绑定都使用 x:DataType.
    • 确保所有代码数据绑定都将所有基于字符串的绑定替换为基于 lambda 的绑定。
  • 将所有 OnPlatform XAML 标记扩展用法替换为使用 OnPlatform<T> 类的实现。 有关详细信息,请参阅 基于平台自定义 UI 外观。
  • 将所有 OnIdiom XAML 标记扩展用法替换为使用 OnIdiom<T> 类的实现。 有关详细信息,请参阅 基于设备成语自定义 UI 外观。
  • 将所有 [QueryProperty(...)] 用法替换为 IQueryAttributable 接口的实现。 有关更多信息,请参阅使用单一方法处理导航数据
  • 将所有 SearchHandler.DisplayMemberName 用法替换为一个 ItemTemplate。 有关详细信息,请参阅 “定义搜索结果项”外观
  • 将 XAML 中使用的类型的所有隐式转换运算符替换为 TypeConverter,并使用 TypeConverterAttribute 将其附加到您的类型。 有关详细信息,请参阅 定义 TypeConverter 以替换隐式转换运算符
    • A类型转换到B类型时,要么使用与A关联的类型转换器上的ConvertTo方法,要么使用与B关联的类型转换器上的ConvertFrom方法。
    • 当源类型和目标类型都有关联的类型转换器时,可以使用其中任一类型转换器。
  • 使用源生成器编译所有正则表达式。 有关详细信息,请参阅 .NET 正则表达式源生成器
  • 确保 JSON 序列化和反序列化使用源生成的上下文。 有关详细信息,请参阅 最小 API 和 JSON 有效负载
  • 查看并更正任何剪裁或 AOT 警告。 有关详细信息,请参阅 剪裁警告简介AOT警告简介
  • 全面测试应用。

iOS 和 Mac Catalyst 上的本地 AOT 诊断支持

本机 AOT 和 Mono 共享诊断和调试功能的子集。 由于 Mono 拥有多种诊断工具,使用它来诊断和调试问题可能十分有用,相较于使用本机 AOT。 剪裁和 AOT 兼容的应用不应有行为差异,因此调查通常同时适用于两种运行时环境。

下表显示了在 iOS 和 Mac Catalyst 上使用本机 AOT 的诊断支持:

功能 完全支持 部分支持 不支持
可观测性和遥测 部分支持
开发时诊断 完全支持
本机调试 部分支持
CPU 分析 部分支持
堆分析 不支持

以下部分提供有关此诊断支持的其他信息。

可观测性和遥测

通过 dotnet-dsrouter 在移动平台上跟踪 .NET MAUI 应用程序,通过 TCP/IP 将诊断工具与 iOS 和 Mac Catalyst 上运行的 .NET 应用程序连接起来。 但是,Native AOT 目前不兼容此场景,因为它不支持基于 TCP/IP 协议栈构建的 EventPipe/DiagnosticServer 组件。 在代码中仍可显式实现可观测性。

开发阶段诊断

.NET CLI 工具为buildpublish提供单独的命令。 dotnet build (或在 Start Debugging (F5) Visual Studio Code 中),在生成或启动 .NET MAUI iOS 或 Mac Catalyst 应用程序时,默认使用 Mono。 仅当在项目文件中启用此部署模型时,dotnet publish才会创建本机 AOT 应用程序。

并非所有诊断工具都可以与已发布的本机 AOT 应用程序无缝配合工作。 但是,所有剪裁和 AOT 兼容的应用程序(即生成时不生成任何剪裁和 AOT 警告)不应在 Mono 和 Native AOT 之间产生行为差异。 因此,在移动应用程序开发周期内,所有 .NET 开发时诊断工具(如 热重载)仍可供开发人员使用。

提示

应像往常一样开发、调试和测试应用程序,并使用 Native AOT 发布最终应用作为最后一个步骤之一。

本地代码调试

在开发过程中运行 .NET MAUI iOS 或 Mac Catalyst 应用程序时,它默认在 Mono 上运行。 在项目文件中启用本机 AOT 部署时,如果应用程序在生成时没有产生任何剪裁(trim)和 AOT 警告,则 Mono 和本机 AOT 的行为应当是一致的。 如果应用程序满足此要求,则可以使用标准的 Visual Studio Code 托管调试引擎进行开发和测试,

发布后,Native AOT 应用程序是真正的本机二进制代码,因此无法使用托管调试器进行调试。 但是,本机 AOT 编译器生成的完全本机可执行文件可以用 lldb 进行调试。 调试 Mac Catalyst 应用 lldb 是很简单的,因为它在同一系统上执行。 但是,调试 NativeAOT iOS 应用程序需要额外的工作量。

使用本机 AOT 调试 .NET MAUI iOS 应用程序

可以按如下方式调试与本机 AOT 兼容的 .NET MAUI iOS 应用程序,这些应用程序已使用此部署模型正确配置并发布:

  1. 使用原生 AOT ios-arm64 目标发布应用,并注意以下信息:

    • 应用程序名称(如下所示 <app-name>)。
    • 包标识符(如下所示 <bundle-identifier>)。
    • 已发布应用程序的存档 .ipa 文件的路径(如下所示 <path-to-ipa>)。
  2. 获取您的物理设备的 ID(如下所示 <device-identifier>):

    xcrun devicectl list devices
    
  3. 在物理设备上安装应用:

    xcrun devicectl device install app --device <device-identifier> <path-to-ipa>
    
  4. 在物理设备上启动应用:

    xcrun devicectl device process launch --device <device-identifier> --start-stopped <bundle-identifier>
    
  5. 打开 lldb 并连接到物理设备:

    (lldb) device select <device-identifier>
    (lldb) device process attach -n <app-name>
    

成功完成这些步骤后,可以使用lldb开始调试本机 AOT .NET MAUI iOS 应用程序。

符号文件的重要性

默认情况下,调试符号将从应用程序的二进制文件剥离到 .dSYM 文件中。 调试器和故障后分析工具使用此文件来显示有关局部变量、源代码行号的信息,并重新生成崩溃转储的堆栈跟踪。 因此,在将应用程序提交到 App Store 之前,必须保留符号文件。

CPU 性能分析

Xcode Instruments 可用于收集本机 AOT 应用程序的 CPU 样本。

堆分析

本机 AOT 目前不支持堆分析。

另请参阅