Unity 构建过程使用称为 Unity 链接器 (Unity linker) 的工具剥离托管代码。Unity 链接器是专为与 Unity 配合使用的 IL Linker 版本。Unity 链接器的自定义 Unity 引擎特定部分未公开使用。
Unity 链接器负责托管代码剥离和引擎代码剥离过程的一部分,这是通过__ IL2CPP__种由 Unity 开发的脚本后端,可在为某些平台构建项目时替代 Mono。更多信息
See in Glossary 脚本后端实现的单独过程,用于删除未使用的引擎代码。有关更多信息,请参阅 PlayerSettings.StripEngineCode。
Unity 链接器将分析项目中的所有程序集。首先,它会标记根类型、方法、属性和字段。例如,添加到场景 (Scene) 中游戏对象的 MonoBehaviour 派生类是根类型。
然后,Unity 链接器分析已标记为要进行识别的根,并标记这些根所依赖的托管代码。完成此静态分析后,所有剩余的未标记代码都无法通过应用程序代码中的任何执行路径来访问,Unity 链接器将从程序集中将其删除。
Unity 编辑器会创建包含了 Unity 项目任何场景所使用类型的程序集列表,并将其传递给 Unity 链接器。然后,Unity 链接器会处理这些程序集、这些程序集的任何引用、link.xml 文件中声明的任何程序集以及具有 AlwaysLinkAssembly 特性的任何程序集。通常,Unity 链接器不会处理项目中包含的不属于这些类别的程序集,也不会将它们排除在播放器构建之外。
Unity 链接器在处理每个程序集时遵循一组规则,这组规则基于程序集分类、程序集是否包含场景中使用的类型以及为构建选择的托管剥离级别 (Managed Stripping Level)。
根据这些规则的用途,程序集划为以下几种分类:
在 Unity 中构建项目时,构建过程将 C# 代码编译为称为公共中间语言 (CIL) 的 .NET 字节码格式。Unity 会将此 CIL 字节码打包到称为程序集的文件中。.NET 框架库以及在项目中使用的插件中的所有 C# 库也都会预先打包为 CIL 字节码的程序集。
在 Unity 链接器执行静态分析时,它会遵循规则集来确定 Unity 链接器会将 CIL 字节码的哪些部分标记为构建所需的部分。根标记规则决定了 Unity 链接器如何在构建中识别和保留顶级程序集。依赖关系标记规则决定了 Unity 链接器如何识别和保留根程序集所依赖的任何代码。
Managed Stripping Level 属性会更改 Unity 链接器使用的规则集。以下部分将介绍 Managed Stripping Level 属性的每个可能设置的标记规则。
下表介绍了 Unity 链接器如何识别程序集中的顶级类型:
|程序集类型:||||标记规则:|
| :— | :— | :— | :— | :— |
|
| 最低 | 低 | 中 | 高 |
|.NET 类和平台 SDK 以及 UnityEngine 程序集| 采用预防性保留以及任何 link.xml 文件中定义的任何保留。 | 采用预防性保留以及任何 link.xml 文件中定义的任何保留。 | 采用任何 link.xml 文件中定义的任何保留。 | 采用任何 link.xml 文件中定义的任何保留。 |
|包含场景中引用的类型的程序集| 标记程序集中的所有类型和成员。 | 标记程序集中的所有类型和成员。 | 标记以下内容:
• 具有 [RuntimeInitializeOnLoadMethod] 或 [Preserve] 特性的所有方法。
• 任何 link.xml 文件中定义的保留。
• 在预编译程序集、软件包程序集、Unity 脚本程序集或程序集定义程序集中标记派生自 MonoBehaviour 和 ScriptableObject 的所有类型。 | 标记以下内容:
• 具有 [RuntimeInitializeOnLoadMethod] 或 [Preserve] 特性的所有方法。
• 任何 link.xml 文件中定义的保留。
• 在预编译程序集、软件包程序集、Unity 脚本程序集或程序集定义程序集中标记派生自 MonoBehaviour 和 ScriptableObject 的所有类型。 |
|其他全部| 标记程序集中的所有类型和成员。 | 标记以下内容:
• 所有公共类型及其公共成员。
• 具有 [RuntimeInitializeOnLoadMethod] 或 [Preserve] 特性的所有方法。
• 任何 link.xml 文件中定义的保留。
• 预编译程序集、软件包程序集、Unity 脚本程序集或程序集定义程序集中派生自 MonoBehaviour 和 ScriptableObject 的所有类型。 | 标记以下内容:
• 所有公共类型及其公共成员。
• 具有 [RuntimeInitializeOnLoadMethod] 或 [Preserve] 特性的所有方法。
• 任何 link.xml 文件中定义的保留。
• 预编译程序集、软件包程序集、Unity 脚本程序集或程序集定义程序集中派生自 MonoBehaviour 和 ScriptableObject 的所有类型。 | 标记以下内容:
• 具有 [RuntimeInitializeOnLoadMethod] 或 [Preserve] 特性的所有方法。
• 任何 link.xml 文件中定义的保留。
• 预编译程序集、软件包程序集、Unity 脚本程序集或程序集定义程序集中派生自 MonoBehaviour 和 ScriptableObject 的所有类型。 |
|测试| 标记具有 [UnityTest] 特性的任何方法以及使用 NUnit.Framework 中定义的特性进行注释的任何方法。 |||||
Unity 链接器识别程序集的根后,它需要识别这些根所依赖的任何代码。下表介绍了 Unity 链接器如何识别程序集内根类型的依赖关系:
|规则目标||||每个剥离级别的操作|
| :— | :— | :— | :— | :— |
|
| 最低 | 低 | 中 | 高 |
|MonoBehaviour| Unity 链接器在标记 MonoBehavior 类型时标记该类型的所有成员。 ||||
|ScriptableObject| Unity 链接器在标记 ScriptableObject 类型时标记该类型的所有成员。 ||||
|特性| Unity 链接器标记程序集、类型或其他代码结构时,也会标记这些结构的所有特性。 | Unity 链接器标记程序集、类型或其他代码结构时,也会标记这些结构的所有特性。 | Unity 链接器标记程序集、类型或其他代码结构时,仅当特性类型也被标记时,才会标记这些结构的特性。 | Unity 链接器标记程序集、类型或其他代码结构时,仅当特性类型也被标记时,才会标记这些结构的特性。 |
|调试特性| 启用脚本调试后,Unity 链接器会标记具有 [DebuggerDisplay] 特性的所有成员,即使没有使用这些成员的代码路径时也是如此。 | 启用脚本调试后,Unity 链接器会标记具有 [DebuggerDisplay] 特性的所有成员,即使没有使用这些成员的代码路径时也是如此。 | Unity 链接器会始终删除调试特性,如 DebuggerDisplayAttribute 和 DebuggerTypeProxyAttribute。 | Unity 链接器会始终删除调试特性,如 DebuggerDisplayAttribute 和 DebuggerTypeProxyAttribute。 |
|.NET 外观类库| 在运行时不需要外观程序集,因此会删除它们。 |||||
Link.xml 文件支持不经常使用的“features”XML 属性。在示例中,mscorlib.dll 中嵌入的 mscorlib.xml 文件使用此特性,但在适当情况下,您可以在任何 link.xml 文件中使用该特性。
在使用高剥离级别时,Unity 链接器根据当前构建的设置来排除对于不受支持功能的保留:
例如,下面的 link.xml 文件在支持 COM 的平台上保留某种类型的一个方法,并在所有平台上保留一个方法:
<linker>
<assembly fullname="Foo">
<type fullname="Type1">
<!--Preserve FeatureOne on platforms that support COM-->
<method signature="System.Void FeatureOne()" feature="com"/>
<!--Preserve FeatureTwo on all platforms-->
<method signature="System.Void FeatureTwo()"/>
</type>
</assembly>
</linker>
在使用高剥离级别时,Unity 链接器将编辑方法体,从而进一步减小代码大小。本节总结了 Unity 链接器对方法体所做出的一些值得注意的编辑。
Unity 链接器目前只编辑 .NET 类库程序集中的方法体。在方法体编辑之后,程序集的源代码不再与程序集中经过编译的代码相匹配,因此可能会使调试变得更加困难。
以下列表介绍了 Unity 链接器用于编辑方法体时可以执行的操作: