Unity3D :Unity链接器
推荐:将NSDT场景编辑器加入你的3D工具链
3D工具集:NSDT简石数字孪生
Unity链接器
Unity 生成过程使用称为 Unity 链接器的工具去除托管代码。Unity 链接器是为使用 Unity 而自定义的 IL 链接器版本。Unity 链接器的自定义 Unity 引擎特定部分不公开可用。
Unity 链接器负责托管代码剥离和部分引擎代码剥离过程,这是通过 IL2CPP 脚本后端提供的单独过程,用于删除未使用的引擎代码。有关更多信息,请参阅 PlayerSettings.StripEngineCode]。
Unity 链接器的工作原理
Unity 链接器分析项目中的所有程序集。首先,它标记根类型、方法、属性和字段。例如,添加到场景中的游戏对象的 MonoBehavior 派生类是根类型。 然后,Unity 链接器会分析它已标记以标识的根,并标记这些根所依赖的任何托管代码。完成此静态分析后,通过应用程序代码的任何执行路径都无法访问任何剩余的未标记代码,Unity 链接器会将其从程序集中删除。
Unity 链接器如何剥离程序集
Unity 编辑器会创建一个程序集列表,其中包含 Unity 项目中的任何场景中使用的类型,并将此列表传递给 Unity 链接器。然后,Unity 链接器处理这些程序集、这些程序集的任何引用、链接.xml文件中声明的任何程序集以及具有 AlwaysLinkAssembly 属性的任何程序集。通常,Unity 链接器不会处理项目中包含的不属于这些类别之一的程序集,而是将它们从播放器生成中排除。
对于 Unity 链接器处理的每个程序集,它遵循一组规则,这些规则基于程序集的分类、程序集是否包含场景中使用的类型以及您为构建选择的托管剥离级别。
根据这些规则的用途,程序集划为以下几种分类:
- .NET 类库程序集 — 包括 Mono 类库(如 mscorlib.dll 和 System.dll)以及 .NET 类库外观程序集(如 netstandard.dll)。
- 平台 SDK 程序集 — 包括特定于平台 SDK 的托管程序集。例如,作为通用 Windows 平台 SDK 一部分的 windows.winmd 程序集。
- Unity 引擎模块程序集 — 包括组成 Unity 引擎的托管程序集,例如 UnityEngine.Core.dll。
- 项目程序集 — 包括特定于项目的程序集,例如:
- 脚本程序集,如 Assembly-CSharp.dll
- 预编译的程序集
- 程序集定义程序集
- 软件包程序集
评分规则
在 Unity 中生成项目时,生成过程会将 C# 代码编译为称为公共中间语言 (CIL) 的 .NET 字节码格式。Unity 将此 CIL 字节代码打包到称为程序集的文件中。在项目中使用的插件中的 .NET 框架库和任何 C# 库也预打包为 CIL 字节码的程序集。
当 Unity 链接器执行其静态分析时,它会遵循一组规则来确定 Unity 链接器将 CIL 字节码的哪些部分标记为生成所必需的。根标记规则确定 Unity 链接器如何识别和保留构建中的顶级程序集。依赖项标记规则确定 Unity 链接器如何标识和保留根程序集所依赖的任何代码。
“托管剥离级别”属性更改 Unity 链接器使用的规则集。以下各节介绍“托管剥离级别”属性的每个可能设置的标记规则。
根标记规则
下表描述了 Unity 链接器如何标识程序集中的顶级类型:
装配类型: | 评分规则: | ||||
---|---|---|---|---|---|
极小 | 低 | 中等 | 高 | ||
.NET Class & Platform SDK 和 UnityEngine Assemblies | 应用预防性保留以及任何链接.xml文件中定义的任何保留。 | 应用预防性保留以及任何链接.xml文件中定义的任何保留。 | 应用任何链接.xml文件中定义的任何保留。 | 应用任何链接.xml文件中定义的任何保留。 | |
具有场景中引用类型的程序集 | 标记程序集中的所有类型和成员。 | 标记程序集中的所有类型和成员。 | 标记以下内容: •具有 or 属性的所有方法。 •在任何链接.xml文件中定义的保留。 •在预编译、包、Unity 脚本或程序集定义程序集中标记从 MonoBehavior 和 ScriptableObject 派生的所有类型。 [RuntimeInitializeOnLoadMethod] [Preserve] | 标记以下内容: •具有 or 属性的所有方法。 •在任何链接.xml文件中定义的保留。 •在预编译、包、Unity 脚本或程序集定义程序集中标记从 MonoBehavior 和 ScriptableObject 派生的所有类型。 [RuntimeInitializeOnLoadMethod] [Preserve] | |
所有其他 | 标记程序集中的所有类型和成员。 | 标记以下内容: •所有公共类型和这些类型的公共成员。 •具有 or 属性的所有方法。 •在任何链接.xml文件中定义的保留。 •从预编译、包、Unity 脚本或程序集定义程序集中的 MonoBehavior 和 ScriptableObject 派生的所有类型。 [RuntimeInitializeOnLoadMethod] [Preserve] | 标记以下内容: •所有公共类型和这些类型的公共成员。 •具有 or 属性的所有方法。 •在任何链接.xml文件中定义的保留。 •从预编译、包、Unity 脚本或程序集定义程序集中的 MonoBehavior 和 ScriptableObject 派生的所有类型。 [RuntimeInitializeOnLoadMethod] [Preserve] | 标记以下内容: •具有 or 属性的所有方法。 •在任何链接.xml文件中定义的保留。 •从预编译、包、Unity 脚本或程序集定义程序集中的 MonoBehavior 和 ScriptableObject 派生的所有类型。 [RuntimeInitializeOnLoadMethod] [Preserve] | |
测试 | 标记具有属性的任何方法以及使用 NUnit.Framework 中定义的属性批注的任何方法。[UnityTest] |
依赖关系标记规则
Unity 链接器标识程序集中的根后,它需要标识这些根所依赖的任何代码。下表描述了 Unity 链接器如何标识程序集中根类型的依赖项:
规则目标 | 每个剥离级别的操作 | ||||
---|---|---|---|---|---|
极小 | 低 | 中等 | 高 | ||
单行为 | Unity 链接器在标记 MonoBehavior 类型时会标记该类型的所有成员。 | ||||
脚本化对象 | Unity 链接器在标记类型时会标记该类型的所有成员。 | ||||
属性 | 当 Unity 链接器标记程序集、类型或其他代码结构时,它还会标记这些结构的所有属性。 | 当 Unity 链接器标记程序集、类型或其他代码结构时,它还会标记这些结构的所有属性。 | 当 Unity 链接器标记程序集、类型或其他代码结构时,如果还标记了属性类型,则仅标记这些结构的属性。 | 当 Unity 链接器标记程序集、类型或其他代码结构时,如果还标记了属性类型,则仅标记这些结构的属性。 | |
调试属性 | 启用脚本调试后,Unity 链接器会标记具有该属性的所有成员,即使没有使用这些成员的代码路径也是如此。[DebuggerDisplay] | 启用脚本调试后,Unity 链接器会标记具有该属性的所有成员,即使没有使用这些成员的代码路径也是如此。[DebuggerDisplay] | Unity 链接器始终删除调试属性,例如调试器显示属性和调试器类型代理属性。 | Unity 链接器始终删除调试属性,例如调试器显示属性和调试器类型代理属性。 | |
.NET 外观类库 | 删除外观程序集,因为它们在运行时不是必需的。 |
链接 XML 功能标记排除项
链接.xml文件支持不常用的“功能”XML 属性。在此示例中,嵌入在 mscorlib.dll 中的 mscorlib.xml 文件使用此属性,但您可以在适当时在任何链接.xml文件中使用它。
使用高剥离级别时,Unity 链接器会根据当前版本的设置排除对不支持的功能的保留:
- 远程处理 — 以 IL2CPP 脚本后端为目标时排除。
- sre — 以IL2CPP脚本后端为目标时排除。
- COM — 定位不支持 COM 的平台时排除。
例如,下面的 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 链接器可以执行的编辑方法主体的操作:
- 删除无法访问的分支 - Unity 链接器删除检查 System.Environment.OSVersion.Platform 且当前目标平台无法访问的 If 语句块。
- 仅访问字段的内联方法 - Unity 链接器替换对获取或设置字段的方法的调用,使其直接访问字段。这通常可以完全剥离该方法。使用 Mono 后端时,Unity 链接器仅在允许方法的调用方根据字段的可见性直接访问字段时进行此更改。对于 IL2CPP,可见性规则不适用,因此 Unity 链接器会在适当的情况下进行此更改。
- 返回常量值的内联方法 - Unity 链接器内联调用仅返回常量值的方法。
- 删除空的非返回调用 - Unity 链接器删除对空且具有 void 返回类型的方法的调用。
- 删除空范围 - 当“最终”块为空时,Unity 链接器将删除“尝试/最终”块。删除空调用可以创建空的 Final 块。在方法编辑期间发生这种情况时,Unity 链接器会删除整个 Try/Final 块。发生这种情况的一种情况是,编译器生成 Try/Final 块作为 foreach 循环的一部分,以便调用 Dispose()。
由3D建模学习工作室整理翻译,转载请注明出处!