Managed Strippingしてみた

# はじめに
Unity 2018.3での更新内容の一つにマネージドコードストリッピング機能の標準化があります。
今後、利用する機会が増えるかもしれないため、マネージドコードストリッピングを試したいと思いました。
ソースコードの削除に伴う問題などもちらほらと聞いたりするため、そちらも試してみたいと思います。

# 使用したバージョン
Unity 2018.3.0b11

# Managed Stripping Levelについて
新しくなったProject Settingsから **Managed Stripping Level** を切り替えることができます。

![project-settings-01.jpg](https://qiita-image-store.s3.amazonaws.com/0/111339/38652c51-59a1-0e38-743e-21d06caf3c15.jpeg)

Managed Stripping Levelのそれぞれのレベルは以下のようになっています。

|Managed Stripping Level|概要|
|:--|:--|
|Disable|コードの削除を行わない。|
|Low|到達できないコードを削除する。|
|Medium|Lowよりもさらにコードを削減する。<br>同じリフレクションコードでも同じ動作をしない可能性がある。<br>カスタムlink.xmlを用意することで削除対象のコードを含めることができる。|
|High|Mediumよりもさらにコードを削除する。<br>いくつかのメソッドのデバッグコードが動作しない可能性がある。<br>同じリフレクションコードでも同じ動作をしない可能性がある。<br>カスタムlink.xmlを用意することで削除対象のコードを含めることができる。|

正確な内容は以下のUnityのドキュメントをご参照ください。
[Unity - Scripting API: ManagedStrippingLevel](https://docs.unity3d.com/2018.3/Documentation/ScriptReference/ManagedStrippingLevel.html)

# サイズの変化
新規作成したプロジェクトをそれぞれの設定でAPKを作成し、そのサイズを比較してみました。
Scripting BackendはMonoでAPKを作成しています。

|Managed Stripping Level|サイズ|DLLの個数|
|:--|--:|--:|
|Disable|26,394 KB|89|
|Low|21,427 KB|35|
|Medium|21,215 KB|32|
|High|21,145 KB|29|

DisableからLowになったときが一番変化が大きい状態となっていました。
DLLの個数が大きく減っていますが、DLL自体の大きさも大きく変わっているものがありました。

# コードの変化
簡単なリフレクションを利用する処理を書いてみて、そのコードがどのように変化するのか比較してみました。
用意したコードは以下のようなものです。

```cs:CallFunction.cs
using UnityEngine;

public class CallFunction
{
    public CallFunction()
    {
        Debug.Log("Call Constructor");
    }

    public void Func()
    {
        Debug.Log("Call Func");
    }
}
```

```cs:CallerBehaviour.cs
using System;
using UnityEngine;

public class CallerBehaviour : MonoBehaviour
{
    private void Start()
    {
        var t = Type.GetType("CallFunction");
        var o = Activator.CreateInstance(t);
        t.GetMethod("Func").Invoke(o, new object[0]);
    }
}
```
CallFunctionのコンストラクタとFuncメソッドはどこからも直接的に利用されておらず、リフレクションによって間接的に利用されています。
実行するとConsoleタブにログが表示されます。

![console01.jpg](https://qiita-image-store.s3.amazonaws.com/0/111339/912ca21b-e8b9-352e-3187-84fe99b84c82.jpeg)

作成したAPKを解凍し、内部に含まれている **Assembly-CSharp.dll** をilspyなどでデコンパイルして中身を見てみます。
Medium以降のDLLでは、CallFunctionの中身が削除されてしまっています。

|Managed Stripping Level|DLLの中身|
|:--|:--|
|Disable|![ilspy-none.jpg](https://qiita-image-store.s3.amazonaws.com/0/111339/9a6c45b9-c639-19f4-2468-76e02bac7c9d.jpeg)|
|Low|![ilspy-low.jpg](https://qiita-image-store.s3.amazonaws.com/0/111339/a2e30923-80f3-8da3-169e-3872f90bb36f.jpeg)|
|Medium|![ilspy-medium.jpg](https://qiita-image-store.s3.amazonaws.com/0/111339/d2f6e04a-6c95-9950-671a-3edc48db875c.jpeg)|
|High|![ilspy-high.jpg](https://qiita-image-store.s3.amazonaws.com/0/111339/cac66776-3e59-c2d0-230a-fafb0c141ca1.jpeg)|

Assets以下に下記のようなlink.xmlを作成すると削除されてしまっていたコードを残すことができます。

```xml:link.xml
<linker>
    <assembly fullname="Assembly-CSharp">
        <type fullname="CallFunction" preserve="all"/>
    </assembly>
</linker>
```

他にも残したいコードやDLLの内容がある場合は、link.xmlに記載していきます。

# まとめ
- Managed Stripping Levelは、Player Settingsから簡単に設定できる。
- Lowの段階でも大きくアプリサイズが変わっていた。（ほぼ新規作成の状態のプロジェクトの場合。）
- Medium以上の設定にすると直接呼び出されていないメソッドなども削除される。
- link.xmlを作成し、記述することで削除対象になっているメソッドなどを残しておくことができる。


# 参考URL
[Unity - Scripting API: ManagedStrippingLevel](https://docs.unity3d.com/2018.3/Documentation/ScriptReference/ManagedStrippingLevel.html)

[IL2CPP を使ったマネージバイトコードストリップ - Unity マニュアル](https://docs.unity3d.com/ja/current/Manual/IL2CPP-BytecodeStripping.html)