mcpField
D
dotnet-maui-aot

dotnet-maui-aot

github.com/wshaddix开发8667 次安装35 周前更新
代码仓库

关于 dotnet-maui-aot

指导如何为面向 iOS 和 Mac Catalyst 的 .NET MAUI 应用配置并测试 Native AOT,涵盖发布、裁剪、库兼容性及构建验证。

主要功能

  1. 01配置 iOS 和 Mac Catalyst AOT 发布
  2. 02了解裁剪与链接器描述文件
  3. 03识别库兼容性问题
  4. 04用源生成器替代反射
  5. 05在模拟器或设备上测试 AOT 构建

适用场景

  1. 01以 Native AOT 发布 MAUI 应用
  2. 02排查裁剪与 AOT 构建警告
  3. 03替换基于反射的序列化
  4. 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 解释器。由此生成的独立原生二进制文件会链接到平台框架。

工作原理
  1. **IL 编译:**NativeAOT 编译器(ILC)将 .NET IL 编译为原生代码
  2. **摇树优化:**根据对可达类型和方法的静态分析裁剪未使用的代码
  3. **原生链接:**生成的原生代码会与 iOS/Catalyst 框架及最精简的 .NET 运行时链接
  4. **应用包:**最终生成标准 .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 注意事项

  1. 启用 PublishAot 时,务必同时启用裁剪分析器。 AOT 要求进行裁剪。在开发期间设置 <EnableTrimAnalyzer>true</EnableTrimAnalyzer> 和 <EnableAotAnalyzer>true</EnableAotAnalyzer>,以便及早发现问题。
  2. 不要假设所有 NuGet 包都兼容 AOT。 检查包的 .csproj 中是否有 IsAotCompatible,或在构建时查看裁剪/AOT 警告。许多常用包仍在内部使用反射。
  3. 不要在 AOT 中使用 Newtonsoft.Json。 它完全依赖反射。迁移到 System.Text.Json,并使用 [JsonSerializable] 源生成上下文实现兼容 AOT 的序列化。
  4. 不要跳过 AOT 构建的设备测试。 模拟器测试可以发现大多数问题,但实际设备上的行为可能不同——尤其是在启动时序、内存限制和平台服务集成方面。
  5. 不要混淆 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 账户
  • 用于设备测试的预配描述文件和签名证书

参考资料

评论

支持 Markdown