Unity 在其支持的所有平台上提供通用的脚本 API 和体验。但是,某些平台具有固有的限制。为了帮助您了解这些限制,下表描述了哪些限制适用于每个平台和 脚本后端Unity 中支持脚本的框架。根据目标平台,Unity 支持三种不同的脚本后端:Mono、.NET 和 IL2CPP。但是,通用 Windows 平台只支持两种:.NET 和 IL2CPP。 更多信息
参见 术语表
平台(脚本后端) | 提前编译 | 支持线程 |
---|---|---|
Android (IL2CPP) | 是 | 是 |
Android (Mono) | 否 | 是 |
iOS (IL2CPP) | 是 | 是 |
独立 (IL2CPP) | 是 | 是 |
独立 (Mono) | 否 | 是 |
通用 Windows 平台 (IL2CPP) | 是 | 是 |
Web (IL2CPP) | 是 | 否 |
某些平台不允许运行时代码生成。任何依赖于目标设备上即时 (JIT) 编译的托管代码都将失败。相反,您必须提前 (AOT) 编译所有托管代码。通常,这种区别并不重要,但在某些特定情况下,AOT 平台需要额外考虑。
Unity 在 AOT 平台上支持反射。但是,如果编译器无法推断出代码是通过反射使用的,则代码可能在运行时不存在。有关更多信息,请参阅 托管代码剥离。
AOT 平台无法实现 System.Reflection.Emit
命名空间中的任何方法。
由于使用反射,AOT 平台可能会遇到序列化和反序列化问题。如果类型或方法仅通过反射用作序列化或反序列化的部分,则 AOT 编译器无法检测到它需要为该类型或方法生成代码。
对于泛型类型和方法,编译器必须确定使用哪些泛型实例,因为不同的泛型实例可能需要不同的代码。例如,List<int>
的代码与 List<double>
的代码不同。但是,IL2CPP 将共享引用类型使用的代码,因此 List<object>
和 List<string>
将使用相同的代码。
可以引用 IL2CPPUnity 开发的脚本后端,可以作为在为某些平台构建项目时使用 Mono 的替代方案。 更多信息
参见 术语表 在以下情况下未找到编译时的泛型类型和方法
Activator.CreateInstance(typeof(SomeGenericType<>).MakeGenericType(someType));
typeof(SomeGenericType<>).MakeGenericType(someType)).GetMethod(“AMethod”).Invoke(null, null);
typeof(SomeType).GetMethod(“GenericMethod”).MakeGenericMethod(someType).Invoke(null, null);
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.");
}
请注意,当启用“更快(更小)的构建”设置时,只编译泛型代码的单个完全可共享版本。这减少了生成的方法数量,从而减少了编译时间和构建大小,但以牺牲运行时性能为代价。
需要被封送到 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 支持异常筛选器,但是过滤器语句和 catch 块的执行顺序不同,因为 IL2CPP 使用 C++ 异常来实现托管异常。除非过滤器阻止对字段的写入,否则您不会注意到这一点。
IL2CPP 在运行时不支持对 MarshalAs
和 FieldOffset
属性的反射。它在编译时支持这些属性。您应该使用这些属性来进行正确的 平台调用封送。
IL2CPP 不支持 C# dynamic
关键字。此关键字需要 JIT 编译,这在 IL2CPP 中是不可能的。
IL2CPP 不支持 Marshal.Prelink
或 Marshal.PrelinkAll
API 方法。
IL2CPP 不支持 System.Diagnostics.Process
API 方法。对于在桌面平台上需要此功能的情况,请使用 Mono 脚本后端。