关于 dotnet-maui-aot
指导如何为面向 iOS 和 Mac Catalyst 的 .NET MAUI 应用配置并测试 Native AOT,涵盖发布、裁剪、库兼容性及构建验证。
主要功能
- 01配置 iOS 和 Mac Catalyst AOT 发布
- 02了解裁剪与链接器描述文件
- 03识别库兼容性问题
- 04用源生成器替代反射
- 05在模拟器或设备上测试 AOT 构建
适用场景
- 01以 Native AOT 发布 MAUI 应用
- 02排查裁剪与 AOT 构建警告
- 03替换基于反射的序列化
- 04发布前检查 AOT 行为
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ wshaddix/ dotnet-skills --skill dotnet-maui-aot安装后在 Claude Code 中输入/dotnet-maui-aot确认已加载。
详细介绍
dotnet-maui-aot
面向 iOS 和 Mac Catalyst 的 .NET MAUI Native AOT 编译:编译流程、发布配置文件、最高可减少 50% 的应用大小并提升最高 50% 的启动速度、库兼容性缺口、退出机制、裁剪的相互影响(RD.xml、源生成器),以及在设备上测试 AOT 构建。
**版本假设:**以 .NET 8.0+ 为基准。MAUI 的 Native AOT 适用于 iOS 和 Mac Catalyst。Android 使用不同的编译模型(.NET 11 中使用 CoreCLR,.NET 8-10 中使用 Mono/AOT)。
**范围边界:**此技能负责 iOS/Mac Catalyst 上 MAUI 专属的 Native AOT,包括编译流程、发布配置、大小和启动速度优化、MAUI 应用的库兼容性,以及 AOT 构建测试。通用 Native AOT 模式由 [skill:dotnet-native-aot] 负责;AOT 架构决策由 [skill:dotnet-aot-architecture] 负责。
**范围之外:**MAUI 开发模式(项目结构、XAML、MVVM)——参见 [skill:dotnet-maui-development]。MAUI 测试——参见 [skill:dotnet-maui-testing]。WASM AOT(Blazor/Uno)——参见 [skill:dotnet-aot-wasm]。通用 AOT 架构——参见 [skill:dotnet-native-aot]。
交叉引用:[skill:dotnet-maui-development](MAUI 模式)、[skill:dotnet-maui-testing](AOT 构建测试)、[skill:dotnet-native-aot](通用 AOT 模式)、[skill:dotnet-aot-wasm](WASM AOT)、[skill:dotnet-ui-chooser](框架选择)。
iOS/Mac Catalyst AOT 编译流程
iOS 和 Mac Catalyst 上的 Native AOT 会在发布时将 .NET IL 直接编译为原生机器码,因此运行时无需 JIT 编译器或 IL 解释器。由此生成的独立原生二进制文件会链接到平台框架。
工作原理
- **IL 编译:**NativeAOT 编译器(ILC)将 .NET IL 编译为原生代码
- **摇树优化:**根据对可达类型和方法的静态分析裁剪未使用的代码
- **原生链接:**生成的原生代码会与 iOS/Catalyst 框架及最精简的 .NET 运行时链接
- **应用包:**最终生成标准
.app包,其中包含原生可执行文件(不随应用发布 IL 程序集)
发布配置
<PropertyGroup Condition="'$(TargetFramework)' == 'net8.0-ios' Or
'$(TargetFramework)' == 'net8.0-maccatalyst'">
<PublishAot>true</PublishAot>
<StripSymbols>true</StripSymbols>
</PropertyGroup>
# Publish with AOT for iOS
dotnet publish -f net8.0-ios -c Release -r ios-arm64
# Publish with AOT for Mac Catalyst
dotnet publish -f net8.0-maccatalyst -c Release -r maccatalyst-arm64
# Publish for iOS simulator (for AOT testing without device)
dotnet publish -f net8.0-ios -c Release -r iossimulator-arm64
权限和预配
AOT 构建所需的权限和预配配置文件与常规 iOS/Catalyst 构建相同。AOT 本身不需要额外权限。
减小大小
与 iOS 上的解释器/JIT 模式相比,Native AOT 可将应用大小减少最高 50%。大小优化来自:
- **摇树优化:**最终二进制文件仅包含可达代码
- **不携带 IL:**应用包不包含 .NET IL 程序集
- **无需运行时 JIT:**不会打包 JIT 编译器及相关元数据
典型大小对比
| 模式 | 大致大小 | 备注 |
|---|---|---|
| 解释器(.NET 8 iOS 默认模式) | ~60-80 MB | 包含 IL 程序集和解释器 |
| Native AOT | ~30-45 MB | 仅原生二进制文件,不含 IL |
| Native AOT + StripSymbols | ~25-40 MB | 已剥离调试符号 |
**注意:**实际大小优化取决于应用的复杂程度、第三方库的使用情况,以及裁剪后可达代码的数量。大量使用反射的库可能会妨碍积极裁剪,从而降低大小优化效果。
提升启动速度
Native AOT 可使 iOS 和 Mac Catalyst 上的冷启动速度最高提升 50%。启动速度提升来自:
- **无需 JIT 预热:**代码已是原生代码,应用启动时无需编译
- **无需加载 IL:**无需加载和解析 .NET 程序集
- **降低内存压力:**启动期间的工作集更小
测量启动时间
// Instrument startup timing
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(
$"App startup: {elapsed.TotalMilliseconds:F0}ms");
}
}
# Use Xcode Instruments for precise startup measurement
# Time Profiler template → measure "pre-main" + "post-main" time
# Compare AOT vs non-AOT builds on the same device
库兼容性
许多 .NET 库并不完全兼容 AOT。常见的兼容性问题源于:
- **反射:**运行时类型检查、
Type.GetType()、Activator.CreateInstance() - 动态代码生成:
System.Reflection.Emit、System.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 | 部分兼容 | 避免运行时表达式编译 |
检测不兼容的代码
<PropertyGroup>
<EnableAotAnalyzer>true</EnableAotAnalyzer>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
</PropertyGroup>
AOT 分析会产生类似 IL3050(RequiresDynamicCode)和 IL2026(RequiresUnreferencedCode)的警告。使用 AOT 发布之前,请先处理这些警告。
退出机制
完全禁用 AOT
<PropertyGroup>
<PublishAot>false</PublishAot>
</PropertyGroup>
按程序集覆盖修剪设置
如果某个特定库不兼容 AOT,可以在仍为应用其余部分使用 AOT 的同时,防止该库被修剪:
<ItemGroup>
<TrimmerRootAssembly Include="IncompatibleLibrary" />
</ItemGroup>
退出 .NET 11 默认设置
.NET 11 引入了与 AOT 交互的新默认设置:
<PropertyGroup>
<MauiXamlInflator>XamlC</MauiXamlInflator>
</PropertyGroup>
<PropertyGroup>
<UseMonoRuntime>true</UseMonoRuntime>
</PropertyGroup>
修剪的相互影响
Native AOT 需要修剪。当 PublishAot 为 true 时,会自动启用修剪。了解修剪配置对于成功构建 AOT 至关重要。
用于保留反射内容的 ILLink 描述符
**注意:**在 Xamarin/Mono 时代的文档中,这些文件称为“rd.xml”(运行时指令)。在 .NET 8+ Native AOT 中,请改用 ILLink 描述符 XML 文件。
当代码使用修剪器无法静态分析的反射时,请使用 ILLink 描述符 XML 文件保留类型。你也可以在代码中使用 [DynamicDependency] 特性进行精细化保留。
ILLink 描述符 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>
<ItemGroup>
<TrimmerRootDescriptor Include="ILLink.Descriptors.xml" />
</ItemGroup>
[DynamicDependency] 特性(适用于有针对性的保留):
using System.Diagnostics.CodeAnalysis;
// Preserve a specific method on a type
[DynamicDependency(nameof(LegacyConfig.Initialize), typeof(LegacyConfig))]
public void ConfigureApp() { /* ... */ }
// Preserve all public members of a type
[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] 源生成器 |
修剪警告
# Build with detailed trim warnings
dotnet publish -f net8.0-ios -c Release /p:PublishAot=true /p:TrimmerSingleWarn=false
# TrimmerSingleWarn=false shows per-occurrence warnings instead of
# one summary warning per assembly, making it easier to fix issues
常见的修剪警告:
- **IL2026:**带有
RequiresUnreferencedCode的成员——该成员执行的操作无法保证在修剪后正常工作 - **IL2046:**基类与派生类之间的修剪特性不匹配
- **IL3050:**带有
RequiresDynamicCode的成员——该成员会在运行时生成代码(与 AOT 不兼容)
测试 AOT 构建
AOT 构建的行为可能与 Debug/JIT 构建不同。发布之前,请务必在真实设备或模拟器上测试使用 AOT 发布的构建。
仅 AOT 模式下的常见故障
| 故障 | 症状 | 修复方法 |
|---|---|---|
| 缺少类型元数据 | 运行时出现 MissingMetadataException | 将类型添加到 ILLink 描述符,或使用 [DynamicDependency] |
| 方法被裁剪 | MissingMethodException | 添加 [DynamicDependency] 或 ILLink 描述符条目 |
| 动态代码生成 | PlatformNotSupportedException | 替换为源生成器方案 |
| 基于反射的序列化 | 反序列化对象为空或为 null | 使用 [JsonSerializable] 源生成 |
| 程序集扫描 | 运行时缺少服务 | 在 DI 中显式注册服务 |
测试工作流
# 1. Build and publish with AOT for simulator (faster iteration)
dotnet publish -f net8.0-ios -c Release -r iossimulator-arm64
# 2. Install and test on simulator
# (Use Xcode or Visual Studio to deploy the .app to simulator)
# 3. Run smoke tests -- focus on:
# - App startup (no MissingMetadataException)
# - JSON deserialization (all properties populated)
# - Navigation (all pages render)
# - Platform services (biometric, camera, location)
# - Third-party SDK integration
# 4. Test on physical device before release
dotnet publish -f net8.0-ios -c Release -r ios-arm64
# Deploy via Xcode with provisioning profile
CI 集成
# CI pipeline: build AOT and run device tests via 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),请参阅 [skill:dotnet-maui-testing]。
Agent 注意事项
- 启用
PublishAot时,务必同时启用裁剪分析器。 AOT 要求进行裁剪。在开发期间设置<EnableTrimAnalyzer>true</EnableTrimAnalyzer>和<EnableAotAnalyzer>true</EnableAotAnalyzer>,以便及早发现问题。 - 不要假设所有 NuGet 包都兼容 AOT。 检查包的
.csproj中是否有IsAotCompatible,或在构建时查看裁剪/AOT 警告。许多常用包仍在内部使用反射。 - 不要在 AOT 中使用
Newtonsoft.Json。 它完全依赖反射。迁移到System.Text.Json,并使用[JsonSerializable]源生成上下文实现兼容 AOT 的序列化。 - 不要跳过 AOT 构建的设备测试。 模拟器测试可以发现大多数问题,但实际设备上的行为可能不同——尤其是在启动时序、内存限制和平台服务集成方面。
- 不要混淆 MAUI iOS AOT 和 Android AOT。 MAUI Native 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 Developer 账户
- 用于设备测试的预配描述文件和签名证书