Version: 6000.3
语言: 中文
使用 IL2CPP 的托管堆栈跟踪
进入播放模式时代码和场景重新加载

脚本限制

Unity 在所有受支持的平台上提供通用的脚本 API 和体验。然而,一些平台有固有的限制。下表描述了适用于每个平台和脚本后端为Unity中的脚本提供支持的框架。Unity 支持三种不同的脚本后端,具体取决于目标平台:Mono、.NET 和 IL2CPP。但是,通用 Windows 平台仅支持两个:.NET 和 IL2CPP。更多信息
请参阅术语表
:

平台(脚本后端) 提前编译 支持线程
安卓 (IL2CPP) 是的 是的
Android(单声道) 是的
iOS (IL2CPP) 是的 是的
独立 (IL2CPP) 是的 是的
独立(单声道) 是的
通用 Windows 平台 (IL2CPP) 是的 是的
网页 (IL2CPP) 是的

提前编译 (AOT)

某些平台不允许生成运行时代码。任何依赖于目标设备上的实时 (JIT) 编译的托管代码都将失败。相反,必须提前编译所有托管代码 (AOT)。通常,这种区别并不重要,但在少数特定情况下,AOT 平台需要额外考虑。

反射

Unity 支持 AOT 平台上的反射。但是,如果编译器无法推断出代码是通过反射使用的,则代码在运行时可能不存在。有关详细信息,请参阅托管代码剥离

AOT 平台无法实现System.Reflection.EmitNamespace。

序列化

由于使用反射,AOT 平台可能会遇到序列化和反序列化问题。如果类型或方法仅通过反射作为序列化或反序列化的一部分使用,则 AOT 编译器无法检测到它是否需要生成类型或方法所需的代码。

泛型类型和方法

对于泛型类型和方法,编译器必须确定使用哪些泛型实例,因为不同的泛型实例可能需要不同的代码。例如,代码List<int>List<double>.但是,IL2CPP 将共享引用类型的用法代码,因此相同的代码将用于List<object>List<string>.

可以引用泛型类型和方法IL2CPPUnity 开发的脚本后端,在为某些平台构建项目时,可以将其用作 Mono 的替代品。更多信息
请参阅术语表
在以下情况下在编译时找不到:

  1. 在运行时创建新的泛型实例:Activator.CreateInstance(typeof(SomeGenericType<>).MakeGenericType(someType));
  2. 在泛型实例上调用静态方法:typeof(SomeGenericType<>).MakeGenericType(someType).GetMethod("AMethod").Invoke(null, null);
  3. 调用静态泛型方法:typeof(SomeType).GetMethod("GenericMethod").MakeGenericMethod(someType).Invoke(null, null);
  4. 对无法在编译时推断的泛型虚拟函数的某些调用。
  5. 具有深度嵌套泛型值类型的调用,例如Struct<Struct<Struct<...<Struct<int>>>>.

为了支持这些情况,IL2CPP 生成适用于任何类型参数的通用代码。但是,此代码速度较慢,因为它无法确定类型的大小,或者它是引用类型还是值类型。如果需要确保生成更快的泛型方法,请执行以下作:

  • 如果泛型参数始终是引用类型,请将where: class约束。然后,IL2CPP 使用引用类型共享生成回退方法,这不会导致性能下降。
  • 如果泛型参数始终是值类型,请添加where: struct约束。这启用了一些优化,但代码仍然会变慢,因为值类型可以有不同的大小。
  • 创建一个名为UsedOnlyForAOTCodeGeneration并添加对希望 IL2CPP 生成的泛型类型和方法的引用。永远不需要调用此方法。以下示例可确保GenericType<MyStruct>生成:
public void UsedOnlyForAOTCodeGeneration()
{
    // Ensure that IL2CPP will create code for MyGenericStruct
    // using MyStruct as an argument.
    new GenericType<MyStruct>();

    // Ensure that IL2CPP will create code for SomeType.GenericMethod
    // using MyStruct as an argument.
    new SomeType().GenericMethod<MyStruct>();

    public void OnMessage<T>(T value) 
    {
        Debug.LogFormat("Message value: {0}", value);
    }

    // Include an exception so we can be sure to know if this
    // method is ever called.
    throw new InvalidOperationException(
        "This method is used for AOT code generation only. " +
        "Do not call it at runtime.");
}

注意:要仅编译通用代码的单个、完全可共享的版本,请将 IL2CPP 代码生成播放器设置设置为针对代码大小和构建时间进行优化。这减少了生成的方法数量,减少了编译时间和构建大小,但以牺牲运行时性能为代价。

从本机代码调用托管方法

需要封送到 C 函数指针以便可以从本机代码调用的托管方法对 AOT 平台有一些限制:

  • 托管方法必须是静态方法。
  • 托管方法必须具有[MonoPInvokeCallback]属性。
  • 如果托管方法是泛型的,则可能需要使用[MonoPInvokeCallback(Type)]重载以指定必须支持的通用专用化。如果是这样,则类型必须是具有正确数量的泛型参数的泛型实例。可以有多个[MonoPInvokeCallback]属性,如下所示:
// Generates reverse P/Invoke wrappers for NameOf<long> and NameOf<int>
// Note that the types are only used to indicate the generic arguments.
[MonoPInvokeCallback(typeof(Action<long>))]
[MonoPInvokeCallback(typeof(Action<int>))]
private static string NameOfT<T>(T item) 
{
    return typeof(T).Name;
}

无螺纹

某些平台不支持使用线程,因此任何使用System.Threading命名空间在运行时失败。.NET 类库的某些部分还隐式依赖于线程。一个常见的例子是System.Timers.Timer类,这取决于对线程的支持。

异常筛选器

IL2CPP 支持异常过滤器。但是,filter 语句和 catch 块的执行顺序不同,因为 IL2CPP 使用 C++ 异常来实现托管异常。除非筛选器块写入字段,否则这并不明显。

MarshalAs 和 FieldOffset 属性

IL2CPP 不支持反射MarshalAsFieldOffset运行时的属性。它在编译时确实支持这些属性。应使用这些来进行正确的平台调用封送处理

dynamic 关键字

IL2CPP 不支持 C#dynamic关键词。此关键字需要 JIT 编译,而 IL2CPP 无法实现这一点。

元帅.预链接

IL2CPP 不支持Marshal.PrelinkMarshal.PrelinkAllAPI 方法。

System.Diagnostics.Process API

IL2CPP 不支持System.Diagnostics.ProcessAPI 方法。对于在桌面平台上需要这样做的情况,请使用 Mono 脚本后端。

其他资源

使用 IL2CPP 的托管堆栈跟踪
进入播放模式时代码和场景重新加载