UniTask高级技巧:使用PreserveAttribute优化IL2CPP编译

UniTask高级技巧:使用PreserveAttribute优化IL2CPP编译

【免费下载链接】UniTask Provides an efficient allocation free async/await integration for Unity. 【免费下载链接】UniTask 项目地址: https://gitcode.com/gh_mirrors/un/UniTask

在Unity项目开发中,使用IL2CPP(中间语言到C++)编译时,常常会遇到因代码裁剪导致的运行时异常。UniTask作为Unity中高效的异步编程库,提供了PreserveAttribute特性来解决这一问题。本文将详细介绍如何通过PreserveAttribute确保异步操作在IL2CPP环境下的稳定性,并提供实用示例。

为什么需要PreserveAttribute?

IL2CPP编译过程中,Unity会自动裁剪未被直接引用的代码以减小包体大小。然而,异步方法(如async/await生成的状态机)和泛型类型常因反射调用或延迟加载而被误裁,导致MissingMethodExceptionTypeLoadExceptionPreserveAttribute通过显式标记需保留的代码,防止IL2CPP裁剪关键逻辑。

UniTask的测试用例中已包含相关验证,例如src/UniTask/Assets/Tests/Preserve.cs中的PreserveAllowTwice方法,展示了如何避免重复等待导致的异常。

如何使用PreserveAttribute

1. 基础用法

在异步任务后链式调用.Preserve()方法,显式保留任务实例:

// 未使用Preserve:重复等待会抛出异常
var task = UniTask.DelayFrame(5);
await task;
await task; // 抛出InvalidOperationException

// 使用Preserve:允许安全重复等待
var safeTask = UniTask.DelayFrame(5).Preserve();
await safeTask;
await safeTask; // 正常执行,不会重复延迟

2. 结合PlayerLoopTiming

指定运行时阶段的任务同样支持Preserve()

// 示例来自Preserve.cs第45行
var delay = UniTask.DelayFrame(5, PlayerLoopTiming.PostLateUpdate).Preserve();
await delay; // 首次等待:5帧后完成
await delay; // 二次等待:立即完成(因任务已保留)

3. 泛型类型保护

对泛型异步方法,需在声明处添加[Preserve]特性:

[Preserve]
public async UniTask<T> FetchData<T>(string url)
{
    // 网络请求逻辑
}

实战案例:避免重复等待异常

以下测试用例对比了有无Preserve()的行为差异(源自src/UniTask/Assets/Tests/Preserve.cs):

无Preserve的风险

[UnityTest]
public IEnumerator AwaitTwice() => UniTask.ToCoroutine(async () =>
{
    var delay = UniTask.DelayFrame(5);
    await delay;
    
    try
    {
        await delay; // 第二次等待抛出异常
        Assert.Fail("should throw exception.");
    }
    catch (InvalidOperationException)
    {
        // 预期异常:任务未保留,无法重复等待
    }
});

有Preserve的安全处理

[UnityTest]
public IEnumerator PreserveAllowTwice() => UniTask.ToCoroutine(async () =>
{
    var delay = UniTask.DelayFrame(5, PlayerLoopTiming.PostLateUpdate).Preserve();
    var before = Time.frameCount;
    
    await delay;
    var afterOne = Time.frameCount; // 5帧后完成
    
    await delay;
    var afterTwo = Time.frameCount; // 立即完成(无额外延迟)
    
    (afterOne - before).Should().Be(5);
    afterOne.Should().Be(afterTwo); // 验证二次等待无延迟
});

注意事项

  1. 性能权衡:过度使用Preserve()可能增加包体大小,仅对需重复使用或反射调用的任务使用。
  2. 版本兼容性:UniTask 2.0+已内置Preserve()扩展方法,低版本需手动添加特性定义。
  3. 测试验证:建议在IL2CPP环境下运行Preserve.cs中的测试用例,确保裁剪后功能正常。

总结

PreserveAttribute是UniTask在IL2CPP环境下稳定运行的关键工具,通过本文介绍的方法,可有效避免代码裁剪导致的异步异常。合理使用.Preserve()方法和[Preserve]特性,结合测试用例验证,能显著提升项目在不同编译环境下的兼容性。

更多UniTask高级特性,请参考官方文档docs/index.md及源码src/UniTask/Runtime/UniTask.cs

提示:点赞收藏本文,下期将介绍"UniTask与Unity Jobs系统的协同使用"。

【免费下载链接】UniTask Provides an efficient allocation free async/await integration for Unity. 【免费下载链接】UniTask 项目地址: https://gitcode.com/gh_mirrors/un/UniTask

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值