最初发现这个问题的情况是,当游戏用IL2CPP平台发布IOS版本的时候,会遇到某些dll格式的插件会导致游戏抛异常崩溃,比如FullInspector和Behavior Designer。所抛的异常是找不到某些类的默认构造函数。
后来发现,不只是某些插件会报这种异常,很多json格式的序列化功能也会在IL2CPP平台上抛找不到默认构造函数的异常。
导致这个问题的原因是,IL2CPP版本在AOT编译时的一些优化机制导致的。想要详细了解这个机制的话可以看文档:http://docs.unity3d.com/Manual/iphone-playerSizeOptimization.html
其实问题就出在函数的静态调用和利用反射进行动态调用的区别上。在AOT阶段编译的时候,凡是检查到某个类或某个函数完全没有使用到,编译器就会抛弃它。这是一个优化机制,但问题就是这种检查只能检查静态引用,对于利用反射机制进行的调用编译器发现不了。这就导致了,没有进行静态引用的函数,会被编译器抛弃掉,然后在企图通过反射机制进行调用的时候,就会导致找不到要调用的函数。
举个例子,有些序列化插件,比如FullSerializer,在生成新对象的时候没有用new(静态引用),而是用了Activator泛型(动态反射)。所以类的构造函数会在编译的时候抛弃掉,然后Activator企图动态访问其动态函数的时候,问题就出现了。
解决这个问题的方法,比较直接的做法在代码中加入丢失的构造函数的静态引用,也就是随便找个地方new一个出来。这样可以防止IL2CPP在优化的时候被抛弃。这种做法的问题是会留一个无用的对象在内存里。
更好的做法是用Unity3D的link.xml功能。这个link.xml的功能就是在AOT编译的时候,告诉编译器,link.xml里指定的这些类,不管有没有引用到,都要保留下来。虽然可以精确指向到某个类,但是比较简单的做法是,把整个出问题的插件的命名空间全部包含进去就可以了。
举个例子,在对应FullInspector和Behavior Designer的时候,link.xml大概是长这个样子的:
<linker>
<assembly fullname="FullInspector-Core">
<namespace fullname="FullInspector" preserve="all"/>
</assembly>
<assembly fullname="BehaviorDesignerRuntime">
< namespace fullname="BehaviorDesigner.Runtime " preserve="all"/>
</assembly>
</linker>
这样这两个插件里的类和函数都会被完整的保留下来了,问题解决。