iOS 和 Mac Catalyst 上的本机 AOT 部署
本机 AOT 部署在 iOS 和 Mac Catalyst 上生成一个 .NET 多平台应用 UI(.NET MAUI)应用,该应用已预先编译为本机代码(AOT)。 本机 AOT 执行静态程序分析、完全修整应用,这在删除未静态引用的代码和提前代码生成方面具有侵略性。
发布和部署本机 AOT 应用会产生以下优势:
- 减小了应用包大小。
- 启动时间更快。
- 生成时间更快。
本机 AOT 将引入对 .NET 运行时某些方面的使用限制,并且只能在应用大小和性能非常重要的情况下使用。 它将要求你根据本机 AOT 要求来调整应用,这意味着确保它们完全剪裁和 AOT 兼容。 有关本机 AOT 限制的详细信息,请参阅 本机 AOT 限制。
启用本机 AOT 部署后,生成系统将分析代码及其所有依赖项,以验证它是否适合完全修整和 AOT 编译。 如果检测到不兼容,则会生成剪裁和 AOT 警告。 单个剪裁或 AOT 警告意味着应用与本机 AOT 部署不兼容,并且可能无法正常工作。 因此,生成用于本机 AOT 部署的应用时,应查看并更正所有剪裁和 AOT 警告。 未能执行此操作可能会导致运行时出现异常,因为可能已删除必要的代码。 如果取消显示警告,则必须对 AOT 部署的应用进行全面测试,以验证功能是否未从未尝试的应用更改。 有关详细信息,请参阅 剪裁警告 简介和 AOT 警告简介。
注意
在某些情况下,修复修整和 AOT 警告是不可能的,例如,当第三方库出现这些警告时。 在这种情况下,需要更新第三方库才能完全兼容。
本机 AOT 性能优势
发布和部署本机 AOT 应用会生成通常小于 2.5 倍的应用,并且启动的应用通常快 2 倍。 但是,确切的性能优势取决于多种因素,包括正在使用的平台、运行应用的设备以及应用本身。
重要
以下图表显示了 iOS 和 Mac Catalyst 上应用的本机 AOT 部署 dotnet new maui
的典型性能优势。 但是,确切的数据依赖于硬件,将来的版本可能会更改。
下图显示了不同部署模型中 iOS 和 Mac Catalyst 应用的应用包大小 dotnet new maui
:
上图显示,与默认部署模型相比,本机 AOT 通常为 iOS 和 Mac Catalyst 生成 2 倍以上的小型应用。
下图显示了 iOS 和 Mac Catalyst on Mono 和 Native AOT 部署上的应用的特定硬件 dotnet new maui
的平均启动时间:
上图显示,与 Mono 部署相比,本机 AOT 在 iOS 设备上通常启动时间最多为 2 倍,在 Mac Catalyst 上启动时间快 1.2 倍。
下图显示了 iOS 和 Mac Catalyst 上应用在不同部署模型中的特定硬件 dotnet new maui
上的平均生成时间:
上图显示,与默认部署模型相比,iOS 设备上的本机 AOT 生成时间通常最高为 2.8 倍。 对于 Mac Catalyst,生成时间与 arm64 单一 RID 应用相当,但与 Mono 部署相比,通用应用的速度略慢。
重要
在许多情况下,本机 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
iOS 和 Mac Catalyst,可在发布期间在生成和本机 AOT 编译期间启用动态代码使用分析。 本机 AOT 分析包括应用的所有代码以及应用依赖的任何库。
警告
$(PublishAot)
生成属性不应按生成配置进行条件。 这是因为根据生成属性的值 $(PublishAot)
启用或禁用剪裁功能开关,并且应在所有生成配置中启用或禁用相同的功能,以便代码的行为相同。 有关剪裁功能开关的详细信息,请参阅 剪裁功能开关。
验证本机 AOT 应用是否正常工作的唯一方法是使用 dotnet publish
和验证代码及其依赖项是否没有剪裁或 AOT 警告。 具体而言, dotnet build -t:Publish
不等效于 dotnet publish
。
dotnet publish
使用以下命令通过本机 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 要求来调整应用,这意味着确保它们完全剪裁和 AOT 兼容,这可能需要大量的工作。 除了本机 AOT 部署的 .NET 限制外,适用于 .NET MAUI 的本机 AOT 部署还有其他限制。
应用依赖的第三方库可能不兼容 AOT。 确保库剪裁且 AOT 兼容的唯一方法是使用本机 AOT 部署和 dotnet publish
命令发布应用,并查看本机 AOT 编译器是否为库生成任何警告。 有关使自己的库 AOT 兼容的信息,请参阅 如何使库与本机 AOT 兼容。
反射和动态代码
本机 AOT 部署限制在代码及其依赖项中使用反射,并且可能需要使用注释来帮助本机 AOT 编译器了解反射模式。 当编译器遇到无法静态分析的反射模式时,因此无法生成应用,则会生成剪裁警告。 本机 AOT 还阻止你在应用中使用动态代码。 例如,编译 System.Linq.Expressions 无法按预期工作,并且无法在运行时加载和执行程序集。 当编译器遇到无法提前编译的动态模式时,它将生成 AOT 警告。
在 .NET MAUI 应用中,这意味着:
- 所有 XAML 都需要预先编译。 因此,请确保尚未禁用 XAML 编译,并编译所有绑定。 有关详细信息,请参阅 XAML 编译 和 编译绑定。
- 所有绑定表达式都必须使用已编译的绑定,而不是设置为字符串的绑定路径。 有关详细信息,请参阅已编译的绑定。
- 将不兼容类型的值分配给 XAML 中的属性时,或者当不同类型的两个属性使用数据绑定时,可能不会调用隐式转换运算符。 相反,应为类型定义一个,并使用 <
a0TypeConverter/a0> 将其附加到该类型。 有关详细信息,请参阅 定义 TypeConverter 以替换隐式转换运算符。 - 不能使用 LoadFromXaml 该方法在运行时分析 XAML。 虽然这可以通过批注可以在运行时使用
DynamicallyAccessedMembers
属性加载的所有类型或DynamicDependency
此属性进行安全修整,但不建议这样做。 - 使用 QueryPropertyAttribute 导航数据接收不起作用。 相反,应在需要接受查询参数的类型上实现 IQueryAttributable 接口。 有关更多信息,请参阅使用单一方法处理导航数据。
- 该
SearchHandler.DisplayMemberName
属性可能不起作用。 相反,应提供 ItemTemplate 来定义 SearchHandler 结果的外观。 有关详细信息,请参阅 “定义搜索结果项”外观。 - 无法使用
OnPlatform
XAML 标记扩展自定义 UI 外观。 应改用 OnPlatform<T> 类。 有关详细信息,请参阅 基于平台自定义 UI 外观。 - 使用
OnIdiom
XAML 标记扩展无法自定义用户界面的外观。 而是应该使用 OnIdiom<T> 类。 有关详细信息,请参阅 基于设备成语自定义 UI 外观。
重要
Mono 解释器与本机 AOT 部署不兼容,因此 $(UseInterpreter)
,使用本机 AOT 时,和 $(MtouchInterpreter)
MSBuild 属性不起作用。 有关 Mono 解释器的详细信息,请参阅 iOS 和 Mac Catalyst 上的 Mono 解释器。
有关剪裁警告的详细信息,请参阅 剪裁警告简介。 有关 AOT 警告的详细信息,请参阅 AOT 警告简介。
将应用适应本机 AOT 部署
使用以下清单帮助你将应用适应本机 AOT 部署要求:
- 确保编译所有 XAML:
- 删除所有
[XamlCompilation(XamlCompilationOptions.Skip)]
用法。 - 删除所有
<?xaml-comp compile="false" ?>
用法。
- 删除所有
- 删除对 LoadFromXaml 该方法的所有调用。
- 确保编译所有数据绑定。 有关详细信息,请参阅已编译的绑定。
- 确保所有 XAML 数据绑定都使用
x:DataType
. - 确保所有代码数据绑定都将所有基于字符串的绑定替换为基于 lambda 的绑定。
- 确保所有 XAML 数据绑定都使用
- 将所有
OnPlatform
XAML 标记扩展用法替换为使用 OnPlatform<T> 类的实现。 有关详细信息,请参阅 基于平台自定义 UI 外观。 - 将所有
OnIdiom
XAML 标记扩展用法替换为使用 OnIdiom<T> 类的实现。 有关详细信息,请参阅 基于设备成语自定义 UI 外观。 - 将所有
[QueryProperty(...)]
用法替换为接口的IQueryAttributable
实现。 有关更多信息,请参阅使用单一方法处理导航数据。 - 将所有
SearchHandler.DisplayMemberName
用法替换为一个 ItemTemplate。 有关详细信息,请参阅 “定义搜索结果项”外观。 - 将 XAML 中使用的类型的所有隐式转换运算符替换为 a,并使用 <
a0TypeConverter/> 将其附加到类型。 有关详细信息,请参阅 定义 TypeConverter 以替换隐式转换运算符。 - 从类型转换为类型
A
B
时,ConvertTo
将使用与A
类型转换器关联的类型转换器上的方法,或者ConvertFrom
将使用与B
类型转换器关联的方法。 - 当源类型和目标类型都有关联的类型转换器时,可以使用其中任一类型转换器。
- 从类型转换为类型
- 使用源生成器编译所有正则表达式。 有关详细信息,请参阅 .NET 正则表达式源生成器。
- 确保 JSON 序列化和反序列化使用源生成的上下文。 有关详细信息,请参阅 最小 API 和 JSON 有效负载。
- 查看并更正任何剪裁或 AOT 警告。 有关详细信息,请参阅 剪裁警告 简介和 AOT 警告简介。
- 全面测试应用。
iOS 和 Mac Catalyst 上的本机 AOT 诊断支持
本机 AOT 和 Mono 共享诊断和检测功能的子集。 由于 Mono 的诊断工具范围,在 Mono 中诊断和调试问题可能很有用,而不是本机 AOT。 剪裁和 AOT 兼容的应用不应具有行为差异,因此调查通常适用于这两个运行时。
下表显示了 iOS 和 Mac Catalyst 上本机 AOT 的诊断支持:
功能 | 完全支持 | 部分支持 | 不支持 |
---|---|---|---|
可观测性和遥测 | 部分支持 | ||
开发时诊断 | 完全支持 | ||
本机调试 | 部分支持 | ||
CPU 分析 | 部分支持 | ||
堆分析 | 不支持 |
以下部分提供有关此诊断支持的其他信息。
可观测性和遥测
通过 dotnet-dsrouter 在移动平台上跟踪 .NET MAUI 应用程序,通过 TCP/IP 将诊断工具与 iOS 和 Mac Catalyst 上运行的 .NET 应用程序连接起来。 但是,本机 AOT 当前与此方案不兼容,因为它不支持使用 TCP/IP 堆栈生成的 EventPipe/DiagnosticServer 组件。 在代码中仍可显式实现可观测性。
开发时诊断
.NET CLI 工具为和build
提供单独的命令publish
。
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 部署,当应用程序在生成时不生成任何剪裁和 AOT 警告时,该行为应在 Mono 和 Native AOT 之间相同。 如果应用程序满足此要求,则可以使用标准的 Visual Studio Code 托管调试引擎进行开发和测试,
发布后,本机 AOT 应用程序是真正的本机二进制文件,因此托管调试器无法处理它们。 但是,本机 AOT 编译器生成可以调试 lldb
的完全本机可执行文件。 使用直接调试 Mac Catalyst 应用 lldb
,因为它在同一系统上执行。 但是,调试 NativeAOT iOS 应用程序需要额外的工作量。
使用本机 AOT 调试 .NET MAUI iOS 应用程序
可以按如下方式调试与本机 AOT 兼容的 .NET MAUI iOS 应用程序,并正确配置和发布此部署模型:
使用本机 AOT 目标
ios-arm64
发布应用,并记下以下信息:- 应用程序名称(如下所示
<app-name>
)。 - 捆绑标识符(如下所示
<bundle-identifier>
)。 - 已发布应用程序的存档 .ipa 文件的路径(如下所示
<path-to-ipa>
)。
- 应用程序名称(如下所示
获取物理设备 ID(如下所示
<device-identifier>
):xcrun devicectl list devices
在物理设备上安装应用:
xcrun devicectl device install app --device <device-identifier> <path-to-ipa>
在物理设备上启动应用:
xcrun devicectl device process launch --device <device-identifier> --start-stopped <bundle-identifier>
打开
lldb
并连接到物理设备:(lldb) device select <device-identifier> (lldb) device process attach -n <app-name>
成功完成这些步骤后,可以使用 开始调试本机 AOT .NET MAUI iOS 应用程序 lldb
。
符号文件的重要性
默认情况下,调试符号将从应用程序的二进制文件剥离到 .dSYM 文件中。 调试器和验尸分析工具使用此文件来显示有关局部变量、源行号的信息,以及重新创建故障转储的堆栈跟踪。 因此,在将应用程序提交到 App Store 之前,必须保留符号文件。
CPU 分析
Xcode Instruments 可用于收集本机 AOT 应用程序的 CPU 样本。
堆分析
本机 AOT 目前不支持堆分析。