Don't ignore the side effect of Managed code stripping
0x00 Description
After Unity 2018.3, the new Managed Stripping Level
option replaces the old Stripping Level
option in the player settings. This new option is available for all platforms and both Mono and IL2CPP scripting backends. The main purpose of this feature is to reduce the size of the app by removing some unused code. It sounds great, but there is a potential side effect. How does Unity know which code is unused code?
0x01 Load From Assetbundle and the script is missing
But before we continue to discuss the pipeline of the code strip, let’s take a look at a scenario where the code strip causes problems.
Now let’s create a cube and a timeline asset in the Editor. Using timeline we can move the cube from point A to point B.
In order to better manage and update resources, we made the cube in the scene into a prefab and removed it from the scene. We then made this prefab and timeline asset into an assetbundle in order to load the resource dynamically.
We can load the assetbundle in the editor and instantiate the prefab, and then we can see that the cube started to move. The timeline scripts worked.
At this point, the workflow appears to be working properly. Remove resources from the scene, make them into assetbundles, and load them dynamically at runtime. Then we build the project to iOS platform, run the same scene and load the same assetbundle to create the cube at runtime. But this time, the cube dosen’t start moving as expected and we get an error message from Unity.
“The referenced script (UnityEngine.Timeline.AnimationTrack) on this Behaviour is missing!”
The timeline scripts are missing and the cube cann’t move on iOS platform.
0x02 The code strip pipeline
The reason why the timeline related scripts are missing is that the relevant code are stripped by Unity when building the iOS player.
The first thing to note is that iOS uses the IL2CPP scripting backend. Mono builds are no longer accepted in the Apple App store and Mono is not supported by iOS 11 and above. Mono can only be selected when using the deprecated .NET 3.5 runtime. When the IL2CPP scripting backend is selected, the Disabled option of Managed code stripping is not available. This means that code strip cannot be turned off when we select the IL2CPP scripting backend.
The second thing to note is the code strip pipeline itself. The Unity build pipeline uses a tool called the UnityLinker
to strip managed code. The process works by defining root assemblies, then using static code analysis to determine what other managed code those root assemblies use. Any code that is not reachable, so-called unused code, is removed. And Unity Engine assemblies may be stripped, too. The root assemblies are those compiled by the Unity Editor from script code (for example, Assembly-CSharp.dll), and the scenes included in the build are used as roots, too.
So there are at least 3 ways to avoid the issue of missing scripts
when loading an asset from an assetbundle at runtime.
-
Reference the required scripts in the scene to prevent the code from being stripped when building the player.
-
Reference the required classes in your script to prevent the code from being strpped when building the player.
-
Add a
link.xml
file to prevent UnityLinker from stripping needed code.
You can check these three ways here.
0x03 Conclusion
If you turn on code strip to reduce the size of the build, then be careful whether Unity strips the code you need or not. Especially for those platforms that use IL2CPP scripting backend, such as iOS, code strip is enabled by default. If you use assetbundles to manage assets, you need to be careful not to remove the required code.