Version: 2022.2
托管堆栈跟踪与 IL2CPP
托管代码剥离

脚本限制

Unity 在支持的所有平台之间提供通用的脚本 API 和体验。但是,有些平台存在固有的限制。为帮助您了解这些限制,下表描述了每个平台和脚本后端适用的限制:

平台(脚本后端) 提前编译 Supports threads
Android (IL2CPP)
Android (Mono)
iOS (IL2CPP)
独立平台 (IL2CPP)
独立平台 (Mono)
通用 Windows 平台 (IL2CPP)
WebGL (IL2CPP)

Ahead-of-time compile (AOT)

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

Reflection

Reflection is supported on AOT platforms. However if this compiler cannot infer that the code is used via reflection the code may not exist at runtime. See [[wiki:ManagedCodeStripping|Managed Code Stripping]] for more information.

System.Reflection.Emit

An AOT platform cannot implement any of the methods in the System.Reflection.Emit namespace.

序列化

AOT platforms might encounter issues with serialization and deserialization because of the use of reflection. If a type or method is only used via reflection as part of serialization or deserialization, the AOT compiler cannot detect that it needs to generate the code needs for the type or method.

Generic Types and Methods

For generic types and methods the compiler must determine which generic instances are used because different generic instances may require different code. For example the code for List<int> is different than it is for List<double>. However IL2CPP will share code for usages for reference types, so the same code will be used for List<object> and List<string>.

It is possible to reference generic types and methods that IL2CPP did not find a compile time in the following cases:

  1. Creating a new generic instance at runtime: Activator.CreateInstance(typeof(SomeGenericType<>).MakeGenericType(someType));
  2. Invoking a static method on a generic instance: typeof(SomeGenericType<>).MakeGenericType(someType)).GetMethod("AMethod").Invoke(null, null);
  3. Invoking a static generic method: typeof(SomeType).GetMethod("GenericMethod").MakeGenericMethod(someType).Invoke(null, null);
  4. Some calls to generic virtual functions that cannot be inferred at compile time.
  5. Calls with deeply nested generic value types. e.g. Struct<Struct<Struct<...<Struct<int>>>>.

To support those cases IL2CPP generates generic code that will work with any type parameter. However this code is slower because it can make no assumptions on the size of the type or if it is a reference or value type. If you need to ensure that faster generic methods are generated you do the following: * If the generic argument will always be a reference type, add the where: class constraint. Then IL2CPP will generate the fallback method using reference type sharing which causes no performance degradation. * If the generic argument will always be a value type, add the where: struct constraint. This enables some optimizations, but since the value types can be different sizes the code will still be slower. * Create a method named UsedOnlyForAOTCodeGeneration and add references to the generic types and methods you wish IL2CPP to generate. This method does not need (and probably shouldn’t) be called. The example below will ensure that a specialization for GenericType<MyStruct> will be generated.

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.");
}

Note that when the “Faster (smaller) builds” setting is enabled only the single fully sharable version of generic code is compiled. This reduces the number of methods generated, reducing compile time and build size, but comes at the expense of runtime performance.

从原生代码调用托管方法

需要编组到 C 函数指针以便可以从原生代码调用的托管方法会在 AOT 平台上有一些限制:

  • 托管方法必须是静态方法
  • 托管方法必须具有 [MonoPInvokeCallback] 属性
  • If the managed method is generic the [MonoPInvokeCallback(Type)] overload may be need to be used to specify the generic specializations that need to be supported. If so the type must be a generic instance with the correct number of generic arguments. It is possible to have multiple [MonoPInvokeCallback] attributes on a method as below:
// 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 supports exception filters, however the execution order filter statements and catch blocks is different because IL2CPP uses C++ exceptions to implement managed exceptions. This will not be noticeable unless a filter blocks writes to a field.

MarshalAs 和 FieldOffset 属性

IL2CPP 不支持在运行时反射 MarhsalAsFieldOffset 属性。它在编译时支持这些属性。应正确使用它们以进行正确的平台调用编组

动态关键字

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

Marshal.Prelink

IL2CPP does not support the Marshal.Prelink or Marshal.PrelinkAll API methods.

System.Diagnostics.Process API

IL2CPP doesn’t support the System.Diagnostics.Process API methods. For cases where this is required on desktop platforms, use the Mono scripting backend.

托管堆栈跟踪与 IL2CPP
托管代码剥离