マネージドコードのストリッピングはビルドから未使用のコードを削除し、最終ビルドサイズを大幅に削減します。IL2CPP スクリプティングバックエンドを使用する場合、C++ に変換してコンパイルするコードが少なくて済むため、マネージコードストリッピングによってビルド時間も短縮できます。マネージコードストリッピングは、プロジェクトの C# スクリプトからビルドされたアセンブリ、パッケージとプラグインの一部であるアセンブリ、.NET Framework のアセンブリなどのマネージアセンブリからコードを削除します。
マネージコードのストリッピングはプロジェクト内のコードを静的に分析することによって機能し、クラス、クラスのメンバー、実行中にアクセスできない関数の部分さえも検出します。アクセスできないコードをどのくらい積極的に取り除くかは、Player Settings ウィンドウ (Optimization セクション内) の Managed Stripping Level 設定で制御できます。
注意 コード (または、プラグインのコード) が、動的にリフレクションを使うクラスやメンバーを検索するとき、コードストリッピングツールは、プロジェクトがそれらのクラスやメンバーを使用していることを常に検知できるわけではありません。そのため、それらを削除する可能性があります。プロジェクトがそのようなコードを使用していることを宣言するには、link.xml ファイル か Preserve 属性を使用します。
どのくらい積極的に使用していないコードを取り除くかは、プロジェクトの Player Settings の Managed Stripping Level オプションを使用して制御します。
注意 このオプションのデフォルト値は、使用する Scripting Backend の設定によって異なります。
プロパティ | 機能 |
---|---|
Disabled | コードは削除されません。 このオプションは、Mono スクリプティングバックエンドのデフォルトのストリッピングレベルです。IL2CPP スクリプティングバックエンドがビルド時に与える影響が理由で IL2CPP スクリプティングバックエンドが選択されている場合、Disabled オプションは使用できません。より多くのマネージコードの使用は IL2CPP を生成する C++ コードが多くなることを意味します。つまり、コンパイルする C++ コードが増えます。その結果、コードを変更してその変更が実際に動くまでに、ずっと長い時間がかかります。 |
Low | 控えめなルールに基づいてコードを削除します。これにより、実際に使用されているコードを削除する可能性を最小限に抑えながら、アクセスできないコードの大部分を削除します。低レベルのストリッピングはサイズの縮小よりも利便性に重きを置いています。 このオプションは、IL2CPP のデフォルトのストリッピングレベルです (Unity エディターの多くのリリースで使用されています)。 |
Medium | Low (低レベル) と High (高レベル) のストリッピングの中間のルールに基づいてコードを削除します。中レベルのストリッピングは低レベルほど慎重ではありませんが、高レベルのストリッピングほど積極的ではありません。そのため、コードの削除による望ましくない副作用が起こるリスクは、低レベルよりも増加しますが高レベルよりは減少します。 コードを変更してからその変更をテストする間のイテレーションの時間をより短縮するには、IL2CPP スクリプティングバックエンドを使った中レベルのストリッピングを使用します。 .NET 3.5 Scripting Runtime Version 設定を使用する場合、中レベルのストリッピングは使用できません。 |
High | 可能な限り多くのアクセスできないコードを削除し、中レベルよりも小さいビルドを生成します。高ストリッピングレベルは、利便性よりもサイズの削減を優先します。link.xml ファイルの追加、Preserve 属性、コード内の問題のある部分の書き換えなどが必要な場合があります。 高ストリッピングレベルにすると、これらの追加のサイズ削減処理を行うために時間がかかる分析が行われるため、ビルドとイテレーションの時間は中レベルよりも長くかかります。 .NET 3.5 Scripting Runtime Version 設定を使用する場合、高ストリッピングレベルは使用できません。 |
注意 Managed Stripping Level オプションは、使用されていない Unity エンジンのコードを削除するプロセスには影響しません (使用されていないコードの削除は、IL2CPP Scripting Backend 設定を使用すると可能です) 。
ここでは、マネージコードストリッピングの詳細を述べ、発生する可能性のある問題を識別し、修正する方法について説明します。
Unity でプロジェクトをビルドすると、ビルドプロセスは C# コードを共通中間言語 (Common Intermediate Language、CIL) と呼ばれる .NET バイトコード形式にコンパイルします。この CIL バイトコードは、アセンブリと呼ばれるファイルにパッケージ化されています。同様に、.NET Framework ライブラリとプロジェクトで使用するプラグインの C# ライブラリも、CIL バイトコードのアセンブリとしてプレパッケージされています。通常、ビルド処理には、プロジェクトが使用するアセンブリのコードの量に関係なく、アセンブリファイル全体が含まれます。
マネージコードのストリッピング処理は、実際に使用されていないコードを検索して削除するために、プロジェクトのアセンブリを分析します。分析は一連のルールを使用して、どのコードを残し、どのコードを破棄するかを決定します。これらのルールは、ビルドサイズ (含まれるコードが多すぎる) とリスク (必要なコードを削除) の妥協点で設定されます。Managed Stripping Level 設定では、コードをどれだけ積極的に削除するかを制御できます。
Unity のビルドプロセスでは、マネージコードを削除するために UnityLinker というツールを使用します。UnityLinker は Mono IL Linker のバージョンの 1 つで、Unity で動作するようにカスタマイズされています。UnityLinker は、プロジェクトから フォーク され、上層の IL Linker プロジェクトを密接に追跡しています。(UnityLinker のカスタムの Unity エンジン特有の部分は、フォーク内で維持されないことに注意してください。)
UnityLinker は、プロジェクト内のすべてのアセンブリを分析します。最初に、最上層、ルートタイプ、メソッド、プロパティ、フィールドなどをマークします。例えば、シーン内のゲームオブジェクトに加える MonoBehaviour 派生クラスはルートタイプです。次に、UnityLinker は、識別するためにマークしたルートを分析し、これらのルートが依存しているすべてのマネージコードをマーキングします。この静的分析が完了した段階で、マーキングされていない残りのコードはアプリケーションコードを通した実行パスによってアクセスできなくなり、アセンブリから削除されます。
この処理はコードを難読化しないことに注意してください。
UnityLinker は、プロジェクトのコードが他のコードを参照するインスタンスを、リフレクション を通して常に検出できるというわけではありません。また、誤って実際は使用するコードを削除することもあります。Managed Stripping Level を Low から High に上げると、コードストリッピングが原因で、ゲーム内で意図しない動作の変化が生じるリスクも増加します。このような動作の変化は、微妙なロジックの変更から、取り除かれたメソッドの呼び出しが原因のクラッシュまでさまざまです。
UnityLinker はいくつかのリフレクションパターンを検出し処理できます。処理可能な最新のパターンの例は、Mono IL Linker リフレクションテストスイート を参照してください。ただし、ちょっとしたリフレクションの使用を超える場合は、UnityLinker に、どのクラスに触れてはいけないかについての指示を与える必要があります。これらの指示は、link.xml ファイルと Preserve 属性の形式で指定できます。
UnityLinker は、アセンブリの使用していないコードの解析をする場合に、属性や link.xml ファイルを使って保持した各要素をルートとして扱います。
ソースコードで [Preserve] 属性を使用して、UnityLinker がそのコードを削除しないようにします。次のリストは、Preserve がさまざまなコード要素に適用される場合に UnityLinker が保持するエンティティを示しています。
Assembly: アセンブリのすべての型を保持します (各型に [Preserve] 属性を設定した場合と同様)。Preserve 属性をアセンブリに割り当てるには、アセンブリに含まれるいずれかの C# ファイルに、名前空間の宣言の外側に、属性宣言を配置します。
using System;
using UnityEngine.Scripting;
[assembly: Preserve]
namespace Example
{
public class Foo {}
}
Type: 型とそのデフォルトコンストラクターを保持します。
Method: メソッド、メソッドの宣言型、戻り値の型、すべての引数の型を保持します。
Property: プロパティ、プロパティの宣言型、値型、getter メソッド、setter メソッドを保持します。
Field: フィールド、フィールドの宣言型、フィールド型を保持します。
Event: イベント、イベントの宣言型、戻り値の型、add メソッド、remove メソッドを保持します。
Delegate: デリゲート型とそのすべてのメソッドを保持します。
link.xml ファイルでコードエンティティをマークすると、Preserve 属性を使用する場合よりもより詳細に制御できます。例えば、クラスに Preserve 属性を加えると、型とデフォルトのコンストラクターの両方を保持します。link.xml ファイルを使用すると、型の保持のみ (デフォルトのコンストラクターは除く) を行うことができます。
Preserve 属性は、すべてのアセンブリと名前空間で定義できます。そのため、UnityEngine.Scripting.PreserveAttribute クラスを使用したり、サブクラス化したり、独自の PreserveAttribute クラスを作成することができます。以下はその例です。
class Foo
{
[UnityEngine.Scripting.Preserve]
public void UsingUnityPreserve(){}
[CustomPreserve]
public void UsingCustomPreserve(){}
[Preserve]
public void UsingOwnPreserve(){}
}
class CustomPreserveAttribute : UnityEngine.Scripting.PreserveAttribute {}
class PreserveAttribute : System.Attribute {}
[assembly: UnityEngine.Scripting.AlwaysLinkAssembly] 属性を使用すると、そのアセンブリがビルドに含まれる別のアセンブリによって参照されているかどうかにかかわらず、UnityLinker に強制的にそのアセンブリを処理させます。AlwaysLinkAssembly 属性は、アセンブリに対してのみ定義できます。
この属性は、Root Marking Rules をアセンブリに適用するように UnityLinker に指示するだけです。属性自体が直接、アセンブリ内のコードを保持するわけではありません。アセンブリのルートマーキングルールと一致するコード要素が全く無い場合、UnityLinker はビルドからアセンブリを削除します。
この属性は、 [RuntimeInitializeOnLoadMethod] 属性を持つメソッドを含むが、プロジェクトのどのシーンでも直接的または間接的に使用する型を含まないパッケージまたはプリコンパイルされたアセンブリに使用します。
アセンブリが [assembly:AlwaysLinkAssembly] を定義し、かつ、ビルドに含まれる別のアセンブリによっても参照されている場合、属性はその結果に影響を与えません。
link.xml ファイルは、アセンブリ、アセンブリ内の型、その他のコードエンティティを保持する方法を宣言するプロジェクトごとのリストです。link.xml ファイルを使用するには、ファイルを作成し (下の例を参照)、Project の Assets フォルダー (または、Assets のサブディレクトリ) に配置します。プロジェクト内では任意の数の link.xml ファイルを使用できます。そのため、プラグインは独自の保持の宣言を提供できます。UnityLinker は link.xml ファイルに保存されているすべてのアセンブリ、型、メンバーをルートタイプとして扱います。
link.xml ファイルはパッケージ内ではサポートされていませんが、パッケージ以外の link.xml ファイルからパッケージアセンブリを参照することができます。
以下の例は、link.xml ファイルを使用して、プロジェクトのアセンブリのルートタイプを宣言できるさまざまな方法を示しています。
<linker>
<!-- アセンブリ内の型とメンバーを保持 -->
<assembly fullname="Assembly1">
<!--すべての型を保持-->
<type fullname="Assembly1.A" preserve="all"/>
<!--"preserve" 属性がなく、 メンバーも特定されない場合は
すべてのメンバーを保持-->
<type fullname="Assembly1.B"/>
<!--1 つの型のすべてのフィールドを保持-->
<type fullname="Assembly1.C" preserve="fields"/>
<!--1 つの型のすべてのメソッドを保持-->
<type fullname="Assembly1.D" preserve="methods"/>
<!--この型だけを保持-->
<type fullname="Assembly1.E" preserve="nothing"/>
<!--任意の型の特定のメンバーだけを保持-->
<type fullname="Assembly1.F">
<!-- フィールド -->
<field signature="System.Int32 field1" />
<!--シグネチャでなく name でフィールドを保持-->
<field name="field2" />
<!-- メソッド -->
<method signature="System.Void Method1()" />
<!--パラメーターによりメソッドを保持-->
<method signature="System.Void Method2(System.Int32,System.String)" />
<!--シグネチャでなく name でメソッドを保持-->
<method name="Method3" />
<!-- プロパティ -->
<!--プロパティ、そのバックのフィールド (存在する場合)、
getter メソッド、setter メソッドを保持-->
<property signature="System.Int32 Property1" />
<property signature="System.Int32 Property2" accessors="all" />
<!--プロパティ、そのバックのフィールド (存在する場合)、getter メソッドを保持-->
<property signature="System.Int32 Property3" accessors="get" />
<!--プロパティ、そのバックのフィールド (存在する場合)、 setter メソッドを保持-->
<property signature="System.Int32 Property4" accessors="set" />
<!--シグネチャでなく name でプロパティを保持-->
<property name="Property5" />
<!-- イベント -->
<!--イベント、そのバックのフィールド (存在する場合)、
add メソッド、remove メソッドを保持-->
<event signature="System.EventHandler Event1" />
<!--シグネチャでなく name でイベントを保持-->
<event name="Event2" />
</type>
<!--Generic の例-->
<type fullname="Assembly1.G`1">
<!--シグネチャの generic によりフィールドを保持-->
<field signature="System.Collections.Generic.List`1<System.Int32> field1" />
<field signature="System.Collections.Generic.List`1<T> field2" />
<!--シグネチャの generic によりメソッドを保持-->
<method signature="System.Void Method1(System.Collections.Generic.List`1<System.Int32>)" />
<!--シグネチャの generic によりイベントを保持-->
<event signature="System.EventHandler`1<System.EventArgs> Event1" />
</type>
<!--ネスト状の型を保持-->
<type fullname="Assembly1.H/Nested" preserve="all"/>
<!--この型が使用されている場合は、この型のすべてのフィールドを保持。
この型が使用されていない場合は、削除。 -->
<type fullname="Assembly1.I" preserve="fields" required="0"/>
<!--この型が使用されている場合は、この型のすべてのメソッドを保持。
この型が使用されていない場合は、削除。-->
<type fullname="Assembly1.J" preserve="methods" required="0"/>
<!--名前空間のすべての型を保持-->
<type fullname="Assembly1.SomeNamespace*" />
<!--name に一般的なプレフィックスがついているすべての型を保持-->
<type fullname="Prefix*" />
</assembly>
<!--アセンブリ全体を保持-->
<assembly fullname="Assembly2" preserve="all"/>
<!-- "preserve" 属性がなく、かつ型の指定がない場合は、すべてを保持-->
<assembly fullname="Assembly3"/>
<!--完全に有効なアセンブリ名-->
<assembly fullname="Assembly4, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<type fullname="Assembly4.Foo" preserve="all"/>
</assembly>
<!--ルートに対してアセンブリを強制的に処理しますが、特に何かを明示的に保持するわけではありません。
アセンブリが参照されていない場合に役立ちます。-->
<assembly fullname="Assembly5" preserve="nothing"/>
</linker>
link.xml ファイルの <assembly> 要素には 3 つの特別な属性があります。
ignoreIfMissing デフォルトでは、link.xml ファイルで参照されるアセンブリが見つからない場合、UnityLinker はビルドを停止します。すべての Player ビルド中に存在しないアセンブリの保持を宣言する必要がある場合は、link.xml ファイルの <assembly> 要素を使用します。
<linker>
<assembly fullname="Foo" ignoreIfMissing="1">
<type name="Type1"/>
</assembly>
</linker>
ignoreIfUnreferenced
そのアセンブリが別のアセンブリによって参照されている場合にのみ、アセンブリのエンティティを保持したい場合があります。link.xml ファイルの <assembly> 要素に対して ignoreIfUnreferenced 属性を使用しすると、少なくとも 1 つの型がアセンブリで参照されている場合に、アセンブリのエンティティを保持します。
<linker>
<assembly fullname="Bar" ignoreIfUnreferenced="1">
<type name="Type2"/>
</assembly>
</linker>
注意 参照元のアセンブリのコードが削除されているかどうかに関係なく、この属性を持つ参照先のアセンブリ内の指定された要素は保持されます。
windowsruntime
Windows Runtime Metadata (.winmd) アセンブリの保持を定義する場合は、link.xml ファイルの <assembly> 要素に windowsruntime = “true” 属性を加える必要があります。
<linker>
<assembly fullname="Windows" windowsruntime="true">
<type name="Type3"/>
</assembly>
</linker>
Unity エディターは、Unity プロジェクトのシーンで使用されている型を含むアセンブリのリストを照合し、UnityLinker に渡します。UnityLinker はそれらのアセンブリ、それらのアセンブリの参照、link.xml ファイルで宣言されたアセンブリ、AlwaysLinkAssembly 属性を持つアセンブリを処理します。一般的に、これらのカテゴリに該当しないプロジェクトに含まれるアセンブリは UnityLinker で処理されず、Player ビルドには含まれません。
UnityLinker が処理する各アセンブリに対し、それはアセンブリの分類 (アセンブリにシーンで使用される型が含まれているかどうか) と、ビルド用に選択した Managed Stripping Level に基づいた一連の規則に従います。
これらの規則の目的のために、アセンブリは以下のように分類されます。
.NET Class Library アセンブリ – mscorlib.dll や System.dll などの Mono クラスライブラリと、netstandard.dll などの .NET クラスライブラリファサードアセンブリが含まれます。
Platform SDK アセンブリ – プラットフォーム SDK 固有のマネージアセンブリが含まれます。例えば、ユニバーサル Windows プラットフォーム SDK の一部である windows.winmd アセンブリです。
Unity Engine Module アセンブリ – UnityEngine.Core.dll など、Unity エンジンを構成するマネージアセンブリを含みます。
Project assemblies – 以下のようなプロジェクト固有のアセンブリを含みます。
Assembly-CSharp.dll などのスクリプトアセンブリ
事前コンパイルされたアセンブリ
パッケージアセンブリ
以下のセクションでは、UnityLinker が Managed Stripping Level 設定ごとにどのようにアセンブリコードをマーキング、保持、または削除するかを説明します。
Managed Stripping Level で Low を選択すると、UnityLinker は控えめなルールに従ってコードを削除します。これにより、実際に使用されているコードを削除する可能性を最小限に抑えながら、アクセスできないコードの大部分を削除します。低 ストリッピングレベルはサイズの縮小よりも利便性に重きを置いています。
ルートマーキングのルールは、UnityLinker がアセンブリ内のトップレベルの型をどのように識別するかを決定します。
アセンブリタイプ | アクション | ルートマーキングルール |
---|---|---|
.NET クラスとプラットフォーム SDK | 削除 | 予防的な保存を適用します |
保存は任意の link.xml で定義されます | ||
シーンで使用する型を含むアセンブリ | コピー | アセンブリ内のすべての型とメンバーをマークします |
他のすべて | 削除 | すべての public タイプをマークします。 |
public タイプのすべてのパブリックメンバーをマークします | ||
[RuntimeInitializeOnLoadMethod] 属性を持つメソッドをマークします | ||
タイプと [Preserve] 属性を持つメンバーをマークします | ||
保存は任意の link.xml で定義されます | ||
以下の MonoBehaviour と ScriptableObject から派生したすべての型をマークします。 事前コンパイルされたアセンブリ パッケージアセンブリ アセンブリ定義アセンブリ Unityスクリプトアセンブリ |
||
テスト | 削除 | NUnit.Framework で定義された任意の属性を持つメソッドをマークします。例 [Test] |
メソッドを [UnityTest] 属性でマークします |
注意 ストリップ は、UnityLinker がアセンブリを解析して削除できるコードを解析することを意味します。コピー は、UnityLinker がアセンブリ全体を最終ビルドにコピーすること (つまり、すべての型をルートタイプとしてマーキングすること) を意味します。
ルートタイプがマークされると、UnityLinker は静的分析を実行して、ルートが依存するすべてのコードを特定します。
ルールのターゲット | Description |
---|---|
Unity 型 | UnityLinker が MonoBehaviour から派生した型をマークすると、その型のすべてのメンバーもマークされます。 |
UnityLinker が ScriptableObject から派生した型をマークすると、その型のすべてのメンバーもマークされます。 | |
属性 | UnityLinker は、マークされたすべてのアセンブリ、型、メソッド、フィールド、プロパティなどの属性をマークします。 |
デバッグの属性 | スクリプトのデバッグを有効にすると、たとえそのメンバーを使用するコードパスがない場合でも、UnityLinker は [DebuggerDisplay] 属性を持つすべてのメンバーをマークします。 |
.NET Facade クラスライブラリアセンブリ | Facade アセンブリは .NET クラスライブラリのアセンブリで、型定義を別のアセンブリに転送します。例えば、.NET Standard 2.0 API Compatibility Level の 1 部である netstandard.dll は、.NET インターフェースを定義するファサードのアセンブリですが、そのインターフェースの実装を他の .NET アセンブリに転送します。ファサードアセンブリは、ランタイムに厳密に必要なわけではありませんが、リフレクションコードを書くことができるため、低ストリッピングレベルはこれらのアセンブリを保持します。 |
次の例では、Foo.UnusedProperty
プロパティをコードのどこにも使用しないと仮定します。通常、UnityLinker はこのプロパティを削除しますが、スクリプトのデバッグを有効にすると、Foo.UnusedProperty
をマークし、Foo
に [DebuggerDisplay] 属性があるため、これを保持します。
[DebuggerDisplay("{UnusedProperty}")]
class Foo
{
public int UnusedProperty { get; set; }
}
Managed Stripping Level で Medium を選択すると、UnityLinker は、低 と 高 のストリッピングレベルの中間にあたる一連のルールに従ってコードを削除します。中 のストリッピングレベルは 低 のストリッピングレベルよりも慎重ではありませんが、高 のストリッピングレベルほど積極的ではありません。そのため、コードを削除することによる望ましくない副作用のリスクは、低 ストリッピングレベルよりも大きく、高 ストリッピングレベルよりも小さくなります。
アセンブリタイプ | アクション | ルートマーキングルール |
---|---|---|
.NET クラスとプラットフォーム SDK | 削除 | 予防的な保持をしないという点を除き、低ストリッピングレベルと同じ |
シーンで参照される型を持つアセンブリ | 削除 | アセンブリ内のすべての型とメンバーを自動的にマークしません |
[RuntimeInitializeOnLoadMethod] 属性を持つメソッドをマークします | ||
タイプと [Preserve] 属性を持つメンバーをマークします | ||
保存は任意の link.xml で定義されます | ||
以下の MonoBehaviour と ScriptableObject から派生したすべての型をマークします。 事前コンパイルされたアセンブリ パッケージアセンブリ アセンブリ定義アセンブリ Unityスクリプトアセンブリ |
||
他のすべて | 削除 | 以下の点を除き、低ストリッピングレベルと同じ public タイプを自動的にマークしません public タイプの public メンバーを自動的にマークしません |
テスト | 削除 | 低ストリッピングレベルと同じ |
ルールのターゲット | Description |
---|---|
Unity 型 | 低ストリッピングレベルと同じ |
属性 | 低ストリッピングレベルと同じ |
デバッグの属性 | 低ストリッピングレベルと同じ |
.NET Facade クラスライブラリアセンブリ | 低ストリッピングレベルと同じ |
Managed Stripping Level で High を選択すると、UnityLinker は可能な限り多くのアクセスできないコードを削除し、中 ストリッピングレベルよりも小さいビルドを生成します。高 ストリッピングレベルは、利便性よりもサイズの削減を優先します。link.xmlファイルや Preserve 属性を加えたり、コードの問題のある部分を書き直す必要な場合もあります。
アセンブリタイプ | アクション | ルートマーキングルール |
---|---|---|
.NET クラスとプラットフォーム SDK | 削除 | 中レベルのストリッピングレベルと同じ |
シーンで参照される型を持つアセンブリ | 削除 | 中レベルのストリッピングレベルと同じ |
他のすべて | 削除 | 中レベルのストリッピングレベルと同じ |
テスト | 削除 | 低ストリッピングレベルと同じ |
Link.xml ファイルは、たまに使用される機能の XML 属性をサポートします。この例では、mscorlib.dll に埋め込まれた mscorlib.xml ファイルはこの属性を使用しますが、適切な場合は、任意の link.xml ファイルで使用できます。
高 レベルのストリッピングでは、UnityLinker は、現在のビルドの設定に基づいて、サポートされていない機能の保持を除外します。
例えば、以下の link.xml ファイルは、COM をサポートするプラットフォーム上で FeatureOne メソッドを保持し、すべてのプラットフォームで FeatureTwo メソッドを保持します。
<linker>
<assembly fullname = "Foo">
<type fullname = "Type1">
<!-- COM をサポートするプラットフォームで FeatureOne を保持 -->
<method signature = "System.Void FeatureOne()" feature = "com"/>
<!-- すべてのプラットフォームで FeatureTwo を保持 -->
<method signature = "System.Void FeatureTwo()"/>
</type>
</assembly>
</linker>
ルールのターゲット | Description |
---|---|
Unity 型 | 低ストリッピングレベルと同じ |
属性 | マークされたすべてのアセンブリ、型、メンバーで、属性のタイプもマークされている場合は UnityLinker は属性をマークします。 UnityLinker は常にランタイムに必要な属性を保持します。 UnityLinker は、すべてのアセンブリ、型、メンバーから System.Security.Permissions、SecurityPermissionAttribute などのセキュリティ関連の属性を削除します。 |
デバッグの属性 | UnityLinkerは常に、DebuggerDisplayAttributeやDebuggerTypeProxyAttributeなどのデバッグ属性を削除します。 |
.NET Facade クラスライブラリアセンブリ | すべての .NET Facade アセンブリを保持する低レベルと中レベルのストリッピングと異なり、高ストリッピングレベルではすべてのファサードを削除します。なぜなら、それらはランタイムには不要だからです。 ストリッピング後に、ファサードアセンブリが存在すると想定するリフレクションコードは機能しません。 |
高 ストリッピングレベルを設定すると、UnityLinker はコードサイズをさらに削減するためにメソッドのボディを編集します。このセクションでは、UnityLinker がメソッドのボディに対して行う注目すべき編集をまとめています。
現在、UnityLinker は .NET クラスライブラリアセンブリ内のメソッドボディのみを編集します。メソッドボディの編集後、アセンブリのソースコードはアセンブリのコンパイルされたコードと一致しなくなり、デバッグがより困難になります。
UnityLinker は、System.Environment.OSVersion.Platform
をチェックし、現在ターゲットになっているプラットフォームにアクセスできない If ステートメントのブロックを削除します。
UnityLinker はフィールドの値を取得または設定するメソッドへの呼び出しを、フィールドへの直接アクセスで置き換えます。これにより、メソッドを完全に取り除くことが可能になり、サイズの削減に役立ちます。
Mono バックエンドをターゲットにする場合、フィールドの可視性に基づいて、メソッドの呼び出し元がフィールドに直接アクセスできるときのみ、UnityLinker はこの変更を行います。IL2CPP では、可視性のルールは適用されません。そのため、UnityLinker は必要に応じてこの変更を行います。
UnityLinker は単に const 値を返すメソッドをインライン化します。
UnityLinker は、中身が空で戻り値がvoid
の型を持つメソッドの呼び出しを削除します。
Finally ブロックが空の場合、UnityLinker は Try/Finally ブロックを削除します。空の呼び出し を削除すると、空の Finally ブロックを作成します。これがメソッドの編集中に起こると、UnityLinker は Try/Finally ブロック全体を削除します。これが発生するケースの 1 つは、Dispose()
を呼び出すために、コンパイラーが Try/Finally ブロックを foreach ループの一部として生成する場合です。