Unity4X IOS可执行程序超过80MB问题及各版本IL2CPP对比
发表于2017-01-22
有些游戏开发者在App Store上传自己的作品有时候会出现大小超载的问题,一般都是IL2CPP导致的,下面就给大家详细介绍下出现的原因,一起来看看吧。
测试版本: Unity 4.x
都是IL2CPP惹的祸
Unity的IL2CPP技术,会把所有的DLL中的IL代码转换成C++。这造成了最终编译的可执行文件大小激增。一些大的项目,在上传App Store时会出现大小超载的提示:
invalid executable size, the size of your app's executable file app_Name is 94208000 bytes,which
exceeds the maximum allowed size of 80MB.
如何查看IPA的可执行程序文件大小?
在mac下使用命令行:
unzip /XXX/AppName.ipa Payload/AppName.app/AppName
size Payload/AppName.app/AppName
图中的示例中,执行文件大小则是41533440 + 46514176,约87MB。超出规定。
图中的示例中,执行文件大小则是41533440 + 46514176,约87MB。超出规定。
不同Unity版本IL2CPP对比
Unity版本 | C++代码总行数 | 泛型相关行数 | Attribute相关行数 | IPA大小 | 纯64位 | 64位+32位 | 备注 |
4.6.4f1 | 约3302万行 | 约2508万行(75%) | 1984行 | 330MB | 约65MB | 约130MB | 正常运行,包太大 |
4.6.6f1 | 约755万行 | 约156万行 (20%) | 约28万行 | 292MB | 约42MB | 约78MB | 包够小! 泛型BUG闪退,KTDCaption.AddPart |
4.6.7f1 | 约731万行 | 约158万行(21%) | 约28万行 | 293MB | 约43MB | 约81MB | 正常运行!包大小差一点点 |
4.6.8f1 | 约589万行 | 约275万(46%) | 约28万行 | 295MB | 约50MB | 约93MB | 初始化阶段,一处反射BUG闪退,KLTaskSettingCallParse里的GetType(xxx, true) |
4.6.9f1 | 约591万行 | 约276万行(46%) | 约28万行 | 295MB | 约50MB | 约93MB | 初始化阶段,一处反射BUG闪退,KLTaskSettingCallParse里的GetType(xxx, true) |
4.7.1f1 | 约591万行 | 约276万行(46%) | 约28万行 | 295MB | 约50MB | 约95MB | 初始化阶段,一处反射BUG闪退,KLTaskSettingCallParse里的GetType(xxx, true) |
注意:
64位+32位要小于80MB才能允许上传到App Store!
尚未进行代码注入!即无热更新功能
IL2CPP的BUG集中在反射和泛型!执行文件大小跟泛型的使用成正比
我们的代码风格越简洁越不会出现问题!
最新的几个版本4.6.8-4.7.1,IL2CPP已经没有太多变化了
需要进行的优化:
object引用类型的泛型,4.6.6后,会进行泛型共享,但是值类型的泛型,依然会生成非常大量的代码
struct改class! 数组struct[] 会生成大量代码,数组class[]则不会
修复带有值类型的泛型: 如IList<(struct)> 改成IList<(class)>, Dict<(value), (value)> 改成Hashtable
以后经验:
禁止多重泛型、泛型类、泛型接口、泛型委托, 泛型方法用前斟酌
尽可能避免使用反射
不同Unity版本IL2CPP的差异
从Unity 4.6.2开始,之后的所有版本主要精力几乎都集中在IL2CPP的修复和改进。 尝试了不同Unity版本的IL2CPP,发现生成的C++代码有非常大的差异,连生成的策略都很不一样。
苹果公司对执行文件的规定
苹果公司规定,iOS应用执行文件大小有上限限制。 只支持64位的应用,执行文件必须在60MB以下。 要求iPhone 5S或以上,iOS 8.0或以上版本。 支持32位+64位的应用,执行文件必须在80MB以下,要求iOS 6.0以上。
是怎样评估泛型代码占用的?
搜索带有“Generic”字符串的C++文件,用shell命令进行统计。
IL2CPP的重灾区--数组Array和泛型 Generic
非常严重, 4.7.1的IL2CPP优化重点,就是对于对泛型进行共享优化。 比如Dictionary<(KItem), (KWeapon)>,会变成Dictionary<(* void), (* void)> 但是Dictionary<(KItem), (int)>,依然会生成C++代码。