dotnet-maui-aot技能 dotnet-maui-aot

这个技能专注于在 iOS 和 Mac Catalyst 平台上使用 Native AOT 编译技术优化 .NET MAUI 应用程序。它涵盖编译管道配置、性能提升(如大小减少和启动加速)、库兼容性处理、修剪机制以及设备测试。关键词包括 .NET MAUI、iOS、Mac Catalyst、AOT 编译、性能优化、库兼容性、修剪配置。

移动开发 0 次安装 0 次浏览 更新于 3/6/2026

名称: dotnet-maui-aot 描述: “为 iOS/Catalyst 优化 MAUI。本地 AOT 管道、大小/启动增益、库间隙、修剪。” 用户不可调用: false

dotnet-maui-aot

在 iOS 和 Mac Catalyst 上为 .NET MAUI 进行本地 AOT 编译:编译管道、发布配置文件、最多 50% 应用大小减少和最多 50% 启动改进、库兼容性间隙、退出机制、修剪交互(RD.xml、源生成器),以及在设备上测试 AOT 构建。

版本假设: .NET 8.0+ 基线。MAUI 的本地 AOT 在 iOS 和 Mac Catalyst 上可用。Android 使用不同的编译模型(.NET 11 中的 CoreCLR,.NET 8-10 中的 Mono/AOT)。

范围

  • iOS/Mac Catalyst AOT 编译管道
  • MAUI AOT 的发布配置文件配置
  • 大小/启动改进测量
  • MAUI AOT 应用程序的库兼容性间隙
  • 退出机制和修剪交互
  • 在设备上测试 AOT 构建

超出范围

  • MAUI 开发模式(项目结构、XAML、MVVM)——见[技能:dotnet-maui-development]
  • MAUI 测试——见[技能:dotnet-maui-testing]
  • WASM AOT(Blazor/Uno)——见[技能:dotnet-aot-wasm]
  • 通用 AOT 架构——见[技能:dotnet-native-aot]

交叉引用:[技能:dotnet-maui-development] 用于 MAUI 模式,[技能:dotnet-maui-testing] 用于测试 AOT 构建,[技能:dotnet-native-aot] 用于通用 AOT 模式,[技能:dotnet-aot-wasm] 用于 WASM AOT,[技能:dotnet-ui-chooser] 用于框架选择。


iOS/Mac Catalyst AOT 编译管道

在 iOS 和 Mac Catalyst 上的本地 AOT 在发布时将 .NET IL 直接编译为本机机器代码,消除了运行时对 JIT 编译器或 IL 解释器的需求。这产生了一个链接到平台框架的自包含本机二进制文件。

工作原理

  1. IL 编译: .NET IL 由 NativeAOT 编译器(ILC)编译为本机代码
  2. 树摇: 未使用的代码基于可达类型和方法的静态分析被修剪
  3. 本机链接: 生成的本机代码与 iOS/Catalyst 框架和最小 .NET 运行时链接
  4. 应用包: 结果是一个标准 .app 包,带有本机可执行文件(不包含 IL 程序集)

发布配置

<!-- 为 iOS/Mac Catalyst 启用本地 AOT -->
<PropertyGroup Condition="'$(TargetFramework)' == 'net8.0-ios' Or
                          '$(TargetFramework)' == 'net8.0-maccatalyst'">
  <PublishAot>true</PublishAot>
  <!-- 可选:去除调试符号以减小二进制大小 -->
  <StripSymbols>true</StripSymbols>
</PropertyGroup>
# 为 iOS 发布 AOT
dotnet publish -f net8.0-ios -c Release -r ios-arm64

# 为 Mac Catalyst 发布 AOT
dotnet publish -f net8.0-maccatalyst -c Release -r maccatalyst-arm64

# 为 iOS 模拟器发布(用于无需设备的 AOT 测试)
dotnet publish -f net8.0-ios -c Release -r iossimulator-arm64

权利和配置文件

AOT 构建需要与常规 iOS/Catalyst 构建相同的权利和配置文件。AOT 本身不需要特殊权利。

<!-- iOS 权利 (Entitlements.plist) -->
<!-- 标准权利;AOT 不需要特殊条目 -->

大小减少

本地 AOT 在 iOS 上可以实现最多 50% 的应用大小减少,相比解释器/JIT 模式。大小改进来自:

  • 树摇: 只有可达代码包含在最终二进制文件中
  • 无 IL 运送: 应用包不包含 .NET IL 程序集
  • 无运行时 JIT: JIT 编译器及相关元数据不打包

典型大小比较

模式 近似大小 说明
解释器(默认 .NET 8 iOS) ~60-80 MB 包括 IL 程序集 + 解释器
本地 AOT ~30-45 MB 仅本机二进制文件,无 IL
本地 AOT + 去除符号 ~25-40 MB 调试符号已去除

注意事项: 实际大小减少取决于应用复杂性、第三方库使用情况,以及修剪后可达代码量。使用重度反射的库可能阻止激进修剪并减少大小收益。


启动改进

本地 AOT 在 iOS 和 Mac Catalyst 上提供最多 50% 更快的冷启动。启动改进来自:

  • 无 JIT 预热: 代码已是本机;应用启动时无编译
  • 无 IL 加载: 无需加载和解析 .NET 程序集
  • 减少内存压力: 启动期间工作集更小

测量启动

// 仪器启动计时
public partial class App : Application
{
    private static readonly long StartTicks = Stopwatch.GetTimestamp();

    public App()
    {
        InitializeComponent();
        MainPage = new AppShell();

        var elapsed = Stopwatch.GetElapsedTime(StartTicks);
        System.Diagnostics.Debug.WriteLine(
            $"应用启动: {elapsed.TotalMilliseconds:F0}ms");
    }
}
# 使用 Xcode Instruments 进行精确启动测量
# Time Profiler 模板 → 测量 "pre-main" + "post-main" 时间
# 在同一设备上比较 AOT 与非 AOT 构建

库兼容性

许多 .NET 库不 fully AOT-compatible。常见兼容性问题源于:

  • 反射: 运行时类型检查,Type.GetType()Activator.CreateInstance()
  • 动态代码生成: System.Reflection.EmitSystem.Linq.Expressions.Compile()
  • 无源生成器的序列化: 使用反射的 JSON/XML 序列化器

兼容性矩阵

库 / 功能 AOT 状态 解决方案
System.Text.Json(源生成) 兼容 使用 [JsonSerializable] 上下文
System.Text.Json(反射) 不兼容 切换到源生成器
CommunityToolkit.Mvvm 兼容 基于源生成,AOT 安全
Entity Framework Core 部分兼容 预编译查询;避免动态 LINQ
Newtonsoft.Json 不兼容 迁移到带源生成的 System.Text.Json
AutoMapper 不兼容 使用 Mapperly(源生成)
MediatR 部分兼容 显式注册处理程序,避免程序集扫描
HttpClient 兼容 标准用法有效
MAUI Essentials 兼容 平台 API 是 AOT 安全
SQLite-net 兼容 使用 P/Invoke,AOT 安全
Refit 不兼容 使用 Refit 7+(包括源生成器;使用 [GenerateRefitClient] 启用)
FluentValidation 部分兼容 避免运行时表达式编译

检测不兼容代码

<!-- 在开发期间启用 AOT 分析警告 -->
<PropertyGroup>
  <EnableAotAnalyzer>true</EnableAotAnalyzer>
  <!-- 同时启用修剪分析器(AOT 需要修剪) -->
  <EnableTrimAnalyzer>true</EnableTrimAnalyzer>
</PropertyGroup>

AOT 分析产生警告如 IL3050(RequiresDynamicCode)和 IL2026(RequiresUnreferencedCode)。在发布 AOT 前解决这些。


退出机制

完全禁用 AOT

<!-- 禁用本地 AOT(使用解释器/JIT 模式) -->
<PropertyGroup>
  <PublishAot>false</PublishAot>
</PropertyGroup>

按程序集修剪覆盖

当特定库不 AOT 兼容时,可以保留它不被修剪,同时其他部分使用 AOT:

<!-- 保留特定程序集免修剪 -->
<ItemGroup>
  <TrimmerRootAssembly Include="IncompatibleLibrary" />
</ItemGroup>

退出 .NET 11 默认值

.NET 11 引入与 AOT 交互的新默认值:

<!-- 恢复 XAML 源生成(使用遗留 XAMLC) -->
<PropertyGroup>
  <MauiXamlInflator>XamlC</MauiXamlInflator>
</PropertyGroup>

<!-- 恢复 Android 上的 Mono 运行时(与 iOS AOT 无关,但影响整体 MAUI AOT 故事) -->
<PropertyGroup>
  <UseMonoRuntime>true</UseMonoRuntime>
</PropertyGroup>

修剪交互

本地 AOT 需要修剪。当 PublishAot 为 true 时,修剪自动启用。理解修剪配置对成功 AOT 构建至关重要。

用于反射保留的 ILLink 描述符

注意: 在 Xamarin/Mono 时代文档中,这些称为 “rd.xml”(运行时指令)。在 .NET 8+ 本地 AOT 中,使用 ILLink 描述符 XML 文件代替。

当代码使用修剪器无法静态分析的反射时,使用 ILLink 描述符 XML 文件保留类型。也可以在代码中使用 [DynamicDependency] 属性进行细粒度保留。

ILLink 描述符 XML(优先用于批量保留):

<!-- ILLink.Descriptors.xml -- 保留运行时需要的类型 -->
<linker>
  <!-- 保留类型的所有公共成员 -->
  <assembly fullname="MyApp">
    <type fullname="MyApp.Models.LegacyConfig" preserve="all" />
    <type fullname="MyApp.Services.PluginLoader">
      <method name="LoadPlugin" />
    </type>
  </assembly>

  <!-- 保留外部程序集中的所有类型 -->
  <assembly fullname="IncompatibleLibrary" preserve="all" />
</linker>
<!-- 在 .csproj 中注册描述符 -->
<ItemGroup>
  <TrimmerRootDescriptor Include="ILLink.Descriptors.xml" />
</ItemGroup>

[DynamicDependency] 属性(优先用于定向保留):

using System.Diagnostics.CodeAnalysis;

// 保留类型上的特定方法
[DynamicDependency(nameof(LegacyConfig.Initialize), typeof(LegacyConfig))]
public void ConfigureApp() { /* ... */ }

// 保留类型的所有公共成员
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(LegacyConfig))]
public void LoadPlugins() { /* ... */ }

源生成器替代方案

当源生成器不可用时,使用 [DynamicDependency] 属性(如上所示)进行定向保留,而无需 ILLink XML 文件。

优先使用源生成器而非反射,完全避免修剪问题:

反射模式 源生成器替代方案
JsonSerializer.Deserialize<T>() [JsonSerializable] 上下文(System.Text.Json)
Activator.CreateInstance<T>() 显式注册的工厂模式
Type.GetProperties() CommunityToolkit.Mvvm [ObservableProperty]
程序集扫描用于 DI 显式 services.Add*() 注册
AutoMapper 反射映射 Mapperly [Mapper] 源生成器

修剪警告

# 构建带详细修剪警告
dotnet publish -f net8.0-ios -c Release /p:PublishAot=true /p:TrimmerSingleWarn=false

# TrimmerSingleWarn=false 显示每次出现警告而非每个程序集一个总结警告,便于修复问题

常见修剪警告:

  • IL2026: 带有 RequiresUnreferencedCode 的成员——成员执行修剪后不能保证工作的操作
  • IL2046: 基/派生类型间的修剪属性不匹配
  • IL3050: 带有 RequiresDynamicCode 的成员——成员在运行时生成代码(与 AOT 不兼容)

测试 AOT 构建

AOT 构建的行为可能与 Debug/JIT 构建不同。发布前总是在真实设备或模拟器上使用 AOT 发布构建进行测试。

常见 AOT 特有故障

故障 症状 修复
缺少类型元数据 运行时 MissingMetadataException 将类型添加到 ILLink 描述符或使用 [DynamicDependency]
修剪方法 MissingMethodException 添加 [DynamicDependency] 或 ILLink 描述符条目
动态代码生成 PlatformNotSupportedException 替换为源生成器替代方案
基于反射的序列化 反序列化对象为空/null 使用 [JsonSerializable] 源生成
程序集扫描 运行时缺少服务 在 DI 中显式注册服务

测试工作流

# 1. 构建并发布 AOT 到模拟器(更快迭代)
dotnet publish -f net8.0-ios -c Release -r iossimulator-arm64

# 2. 在模拟器上安装和测试
#(使用 Xcode 或 Visual Studio 将 .app 部署到模拟器)

# 3. 运行冒烟测试——重点关注:
#    - 应用启动(无 MissingMetadataException)
#    - JSON 反序列化(所有属性填充)
#    - 导航(所有页面渲染)
#    - 平台服务(生物识别、摄像头、位置)
#    - 第三方 SDK 集成

# 4. 发布前在物理设备上测试
dotnet publish -f net8.0-ios -c Release -r ios-arm64
# 通过 Xcode 和配置文件部署

CI 集成

# CI 管道:构建 AOT 并通过 XHarness 运行设备测试
dotnet publish -f net8.0-ios -c Release -r iossimulator-arm64 /p:PublishAot=true

xharness apple test \
    --app bin/Release/net8.0-ios/iossimulator-arm64/publish/MyApp.app \
    --target ios-simulator-64 \
    --timeout 00:10:00 \
    --output-directory test-results/aot

对于 MAUI 测试模式(Appium、XHarness),见[技能:dotnet-maui-testing]。


代理注意事项

  1. 不要启用 PublishAot 而不启用修剪分析器。 AOT 需要修剪。设置 <EnableTrimAnalyzer>true</EnableTrimAnalyzer><EnableAotAnalyzer>true</EnableAotAnalyzer> 在开发期间及早发现问题。
  2. 不要假设所有 NuGet 包都 AOT 兼容。 检查包 .csproj 中的 IsAotCompatible 或在构建时查找修剪/AOT 警告。许多流行包仍内部使用反射。
  3. 不要使用 Newtonsoft.Json 与 AOT。 它完全依赖反射。迁移到带 [JsonSerializable] 源生成上下文的 System.Text.Json 以实现 AOT 安全序列化。
  4. 不要跳过 AOT 构建的设备测试。 模拟器测试捕获大多数问题,但物理设备行为可能不同——特别是启动计时、内存约束和平台服务集成。
  5. 不要混淆 MAUI iOS AOT 与 Android AOT。 MAUI 本地 AOT(PublishAot)仅针对 iOS 和 Mac Catalyst。Android 使用不同的编译模型(.NET 8-10 中的 Mono AOT,.NET 11+ 中的 CoreCLR)。它们是分开配置的。

先决条件

  • .NET 8.0+ 带 MAUI 工作负载
  • Xcode 和 iOS/Mac Catalyst SDK(仅 macOS)
  • Apple 开发者账户用于物理设备部署
  • 设备测试的配置文件和签名证书

参考