版本:Unity 6 (6000.0)
语言:英语
托管代码剥离
Unity 编辑器中的代码重载

Unity 链接器

Unity 构建过程使用一个名为 Unity 链接器的工具来剥离托管代码。Unity 链接器是 IL 链接器 的一个定制版本,专门用于与 Unity 协同工作。Unity 链接器中 Unity 引擎特有的定制部分不公开提供。

Unity 链接器负责托管代码剥离和引擎代码剥离过程的一部分,后者是一个独立的过程,可通过 IL2CPPUnity 开发的脚本后端,可作为构建某些平台项目的 Mono 替代方案。 更多信息
参见 术语表
脚本后端使用,该后端会删除未使用的引擎代码。有关更多信息,请参见 PlayerSettings.StripEngineCode

Unity 链接器的工作原理

Unity 链接器会分析项目中的所有程序集。首先,它会标记根类型、方法、属性和字段。例如,您添加到 游戏对象Unity 场景中的基本对象,可以代表角色、道具、场景、摄像机、航路点等。游戏对象的功​​能由附加到它的组件定义。 更多信息
参见 术语表
的 MonoBehaviour 派生类 场景场景包含游戏环境和菜单。将每个独特的场景文件视为一个独特的关卡。在每个场景中,您可以放置环境、障碍物和装饰,本质上是分段设计和构建游戏。 更多信息
参见 术语表
中的根类型。然后,Unity 链接器会分析它标记的根,识别并标记这些根所依赖的任何托管代码。完成此静态分析后,任何剩余的未标记代码都无法通过应用程序代码的任何执行路径访问,Unity 链接器会将其从程序集中删除。

Unity 链接器如何剥离程序集

Unity 编辑器会创建一个包含 Unity 项目中任何场景使用的类型的程序集列表,并将此列表传递给 Unity 链接器。然后,Unity 链接器会处理这些程序集、这些程序集的任何引用、link.xml 文件中声明的任何程序集以及具有 AlwaysLinkAssembly 属性的任何程序集。一般来说,Unity 链接器不会处理项目中包含的但不属于这些类别的程序集,并将它们从 Player 构建中排除。

对于 Unity 链接器处理的每个程序集,它会根据程序集的分类、程序集是否包含场景中使用的类型以及为构建选择的 托管剥离级别 来遵循一组规则。

为了这些规则的目的,程序集分为以下分类

  • .NET 类库程序集 — 包括 Mono 类库(如 mscorlib.dll 和 System.dll),以及 .NET 类库外观程序集(如 netstandard.dll)。
  • 平台 SDK 程序集 — 包含特定于平台 SDK 的托管程序集。例如,作为通用 Windows 平台 SDK 一部分的 windows.winmd 程序集。
  • Unity 引擎模块程序集 — 包括构成 Unity 引擎的托管程序集,例如 UnityEngine.Core.dll。
  • 项目程序集 — 包括特定于项目的程序集,例如

标记规则

在 Unity 中构建项目时,构建过程会将您的 C# 代码编译成名为通用中间语言 (CIL) 的 .NET 字节码格式。Unity 将此 CIL 字节码打包到称为程序集的文件中。您在项目中使用的插件中的 .NET 框架库和任何 C# 库也作为 CIL 字节码的程序集预先打包。

当 Unity 链接器执行其静态分析时,它会遵循一组规则来确定 Unity 链接器标记为构建必要的 CIL 字节码的部分。根标记规则确定 Unity 链接器如何识别和保留构建中的顶级程序集。依赖项标记规则确定 Unity 链接器如何识别和保留根程序集所依赖的任何代码。

托管剥离级别 属性会更改 Unity 链接器使用的规则集。以下部分描述了 托管剥离级别 属性的每个可能设置的标记规则。

根标记规则

下表描述了 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 链接器标记类型时,它也会标记 MonoBehaviour 类型的全部成员。
ScriptableObject 当 Unity 链接器标记类型时,它也会标记 ScriptableObject 类型的全部成员。
属性 当 Unity 链接器标记程序集、类型或其他代码结构时,它也会标记这些结构的所有属性。 当 Unity 链接器标记程序集、类型或其他代码结构时,它也会标记这些结构的所有属性。 当 Unity 链接器标记程序集、类型或其他代码结构时,它只会标记这些结构的属性,前提是属性类型也已标记。 当 Unity 链接器标记程序集、类型或其他代码结构时,它只会标记这些结构的属性,前提是属性类型也已标记。
调试属性 启用脚本调试时,Unity 链接器会标记所有具有 [DebuggerDisplay] 属性的成员,即使没有使用这些成员的代码路径。 启用脚本调试时,Unity 链接器会标记所有具有 [DebuggerDisplay] 属性的成员,即使没有使用这些成员的代码路径。 Unity 链接器始终会删除调试属性(如 DebuggerDisplayAttribute 和 DebuggerTypeProxyAttribute)。 Unity 链接器始终会删除调试属性(如 DebuggerDisplayAttribute 和 DebuggerTypeProxyAttribute)。
.NET 外观类库 删除外观程序集,因为它们在运行时不需要。

Link XML 功能标记排除

Link.xml 文件支持很少使用的“features”XML 属性。例如,mscorlib.dll 中嵌入的 mscorlib.xml 文件使用此属性,但您可以在需要时将其用于任何 link.xml 文件中。

使用 剥离级别时,Unity 链接器会排除针对当前构建设置不支持的功能的保留。

  1. remoting — 针对 IL2CPP 脚本后端支持 Unity 中脚本的框架。Unity 根据目标平台支持三种不同的脚本后端:Mono、.NET 和 IL2CPP。但是,通用 Windows 平台仅支持两种:.NET 和 IL2CPP。 更多信息
    参见 术语表
    时排除。
  2. sre — 针对 IL2CPP 脚本后端时排除。
  3. 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 链接器会删除 Finally 块为空的 Try/Finally 块。删除 空调用 会创建空的 Finally 块。当在方法编辑期间发生这种情况时,Unity 链接器会删除整个 Try/Finally 块。这种情况可能发生在编译器为了调用 Dispose() 而为 foreach 循环生成 Try/Finally 块时。

其他资源

托管代码剥离
Unity 编辑器中的代码重载