问题
- 应用大小超过 iOS 移动网络下载的限制会强制 iOS 用户通过 WiFi 下载。
- 应用的通用安装包或者单独包均大于苹果提交指引的要求。
原因
我的应用大小超过了 iOS 移动网络下载的限制并且超过了 tvOS 主应用的大小。
otool的报告反馈单独的应用包(32bit, 64bit)或者是通用包都超过了苹果提交审核的要求。
解决方案
IL2CPP将C#的预编代码转换C++代码,这些代码将会被Xcode和LLVM转换成最终的执行代码,这会导致更大的通用包或者应用分片(32bit,64bit)尺寸。您可以通过几个方面来优化代码。阅读下面的信息将更有助您了解可以做哪些工作来优化构建包的大小:
- 如果您使用的是通用构建,那将会有32bit分片和64bit分片两个部分,而这两个部分为两种不同的架构包含了两个完全一样的可执行文件,导致尺寸增大一倍。
- 如果您对比过ARMv7 Mono 和ARMv7 IL2CPP的话,IL2CPP的版本会更大些;造成这样是因为要处理类型的元数据。这在Unity 4.6.4p2中已被修复。在4.7.0f1和5.4.0f1中都做了进一步的改进。
相比于只针对ARMv7或者ARM64架构,针对通用架构进行构建总会生成更大尺寸的包。移除托管组件集已经可以使构建更小(Unity 4.6.3f1), 但是真正的改进来自于我们在运行时中可以至少创建通用的typeinfo (generic typeinfo),不过这个功能目前在Unity 4.6.4p1中已经可以部分实现了。 相比于Mono 32bit,包含了32-bit分片和64-bit 分片的通用构建尺寸要大上一倍 。
请在mono (ARMv7), IL2CPP (ARMv7), IL2CPP (ARM64), IL2CPP Universal之间做个对比表格,包括使用了哪些Stripping设置的信息!
苹果会检查哪些大小的限制?
请点击这里查阅查苹果应用商店的提交信息。
- 对于MinimumOSVersion 小于0 的应用, 二进制包__TEXT 部分最大是80MB。
- 对于MinimumOSVersion 是x 到8.x 的应用, 二进制包中每种架构__TEXT 部分最大是60MB。
- 对于MinimumOSVersion 是x 或者更高的应用, Mach-O 二进制包中最大是400 MB。
如果您支持的iOS系统小于iOS 7.0, 苹果对于32bit+64bit代码部分一共有 80MB 的大小限制。对于iOS系统版本最小是7.0或者更高的,针对每个架构的分片有60MB(60 000 000 字节)的大小限制。100MB(移动数据)应用限制(用户需要通过 WiFi 下载)和最大 4GB 的应用限制。 80MB 和 60MB/60MB是针对包含在fat二进制里的32bit和64bit分片的大小限制。您需要用otool以获得准确的数字。.ipa 文件的大小限制在100MB,这个大小决定了用户是否需要通过 WiFi去下载您的应用。通过以下这些步骤以获得准确的数字:
-
在发布模式下构建您的应用。不要用测试模式,因为它无法代表您的最终安装包。确认您使用了正确的优化方法,可以点击此处了解更多信息:http://docs.unity3d.com/Manual/iphone-playerSizeOptimization.html。
-
在 XCode 下打包您的应用,并用您的部署证书导出.ipa文件。
-
使用估算按钮来估算应用的大小,以确认应用小于100MB或者整体包的大小小于4GB。
注意:从 Xcode 6.3起就没有估算按钮 了。您可以通过以下公式计算大小,但是要注意压缩系数可能会有所变化:
app_store_size = sizeof(.ipa/(zip with exec_binary removed)) + sizeof(codesegment(exec_binary)) + 0.2 * sizeof(datasegment(exec_binary))
该公式是基于以下的认知:只有代码部分(code segment)会被加密,数据部分(data segment)的压缩率可以很容易被确认 。
使用dd命令从可执行包里面解压出数据 (你可以指定字节偏移和长度),然后再压缩这些数据。代码部分(code segment)被加密成完美的噪声一样。在执行之前你需要一个密钥才能反编译它。iTunes/App Store管理着这些密钥。这就是为什么我们被代码部分(code segment)看做一个整体,而没有将它包含在压缩率的计算里面。
-
当您创建好 .ipa包以后(包体的大小几乎等同于Xcode估计按钮返回的结果),打开终端,找到.ipa的 目录,然后运行otool。
- 使用otool将输出进行解压缩,然后查看是否已经接近80MB的大小限制 。
otool -l <your_app_name>.app/<your_app_name> or size <your_app_name>.app/<your_app_name> -
现在您可以根据不同的架构得到相应的输出(如果是通用架构,则是armv7 + arm64 两部分 ),为armv7 (LC_SEGMENT)收集信息,如果arm64 (LC_SEGMENT_64) 可用的话,你可以做同样的事情。
-
用segname __TEXT定位到LC_SEGMENT
code segment size = 30474240 ~= 30MB - 用segname __DATA定位到 LC_SEGMENT
data segment size (mostly metadata) = 10420224 ~= 10 MB
armv7架构:
code segment size = 30474240 ~= 30MB
data segment size (大部分是元数据) = 10420224 ~= 10 MB
segment + data segment = 30 + 10 = 40MB
arm64架构:
code segment size = 25559040 ~= 26MB
data segment size (大部分是元数据) = 17727488 ~= 18 MB
segment + data segment = 26 + 18 = 44MB
苹果使用这个例子中的otool报告(30MB + 26MB = 56MB)来做检测。上述报告在 <7.0的时候,小于80MB;在>=7.0的时候分别是30MB和26MB,两者都是小于60MB。 -
在这些检查的基础之上,就很容易在发布模式下打包一个测试应用,然后提交给iTunes Connect。在提交过程中,如果分片大小超过限制的话,静态项目检查器应该会提醒您。一旦您把应用提交到iTunes Connect,您就可以看到针对所有单个平台和通用平台苹果所期望的压缩后文件大小,下载文件大小和安装文件大小。可以看下面的例子:
使用 iOS 9.0的设备会根据需要只下载相应的部分(32或者64)。苹果会切割这些二进制包并会创建一个独立的包,所以像iPhone 6s这样的设备会得到一个更小的下载包,这个过程中你不需要做任何事情。这跟 ODR 或者 Bitcode 完全没有关系。但是使用 iOS 9.0以下的设备仍然需要下载通用包。
5.3.x由于 Bitcode 的原因构建的大小会变大
当您用 Unity 5.3.x 构建iOS应用的时候,得到的包体会变大。这是因为 5.3.x的 Bitcode 支持已经打开了。为什么这是件好事以及怎么处理可以看这里。
通过 MapFileParser 分析脚本
您可以更深入地了解和查看脚本对包体大小的影响。您可以使用 MapFileParser 这个来检查可执行文件的尺寸以及脚本对其大小所做的贡献。这个工具会在Xcode打包应用的时候,处理Xcode生成的map文件(在使用Archive打包的时候不会生成map文件) 。在 5.2.4p1 及以下版本里,-stats 这个标识符是不存在的。您可以在 Unity.app 的安装目录下找到 MapFileParser 或者Unity生成的 Xcode 工程根目录中也可以找到。在 Xcode 链接输出的Derive Data文件夹中可以找到 your_map_file.map。
完整的路径类似于:Library/Developer/Xcode/DerivedData/Unity-iPhone-glmxdxebssyebsfcbtobeuasetge/Build/Intermediates/Unity-iPhone.build/<mode>-iphoneos/Unity-iPhone.build/alpin-LinkMap-normal-<architecture>.txt
您可以在终端运行下面的命令来分析这些map文件。
<Contents/Tools/MapFileParser/>MapFileParser -format=Clang <your_map_file.map> -stats
分析这些函数的名字是区分不同代码最好的方法(用户脚本,静态库)。从托管集里面生成的代码会以 _m的后缀结尾。例如:
_TestScript_Start_m4164746442: 10 bytes
_TestScript_Update_m263972995: 10 bytes
_TestScript_ctor_m922641354: 24 bytes
这会帮您更好的理解您的代码,插件或者引擎代码。
- 阅读更多关于优化iOS及tvOS应用大小和IL2CPP
- 阅读更多关于IL2CPP的信息
-
阅读更多关于IL2CPP的博文
-
阅读更多关于iOS 64bit的支持
- 阅读更多关于IL2CPP构建大小的改进
本文适用于 Unity 5.2.0p1 及以上,Xcode7及以上,iOS9.0及以上版本