前言
APP 的大小是分为 APP 下载大小和安装大小两个概念的。
下载大小是指 App 压缩包(也就是 .ipa 文件)所占的空间,用户在下载 App 时,下载的是压缩包,这样做可以节省流量;
当压缩包下载完成后,就会自动解压,解压过程也就是通常所说的安装过程;安装大小就是指压缩包解压后所占用的磁盘空间。
就将“安装包”作为了优化指标。“安装包”减小后,“下载包”自然也会减小。
App Store OTA 下载大小限制:
虽然苹果历年都会调整 App 下载大小,由之前的 100M 到后来的 150M 再到现在的 200M。如今,App 下载大小超出 200 MB 时 ,会出现两种情况:
- iOS 13 以下的用户,无法通过蜂窝数据下载 App;
- iOS 13 及以上的用户,需要手动设置才可以使用蜂窝网络下载 App。
Apple __TEXT 段大小限制:
-
iOS 7 之前,二进制文件中所有的 __TEXT 段总和不得超过 80 MB;
-
iOS 7.X 至 iOS 8.X,二进制文件中,每个特定架构中的 __TEXT 段不得超过 60 MB;
-
iOS 9.0 之后,二进制文件中所有的 __TEXT 段总和不得超过 500 MB。
安装包大小增长的影响
AppStore 下载大小如果在 OTA 下载限制内增长,对用户新增、留存等指标影响不大。而一旦超过 OTA 下载限制,则对整体指标产生明显影响。之前统计的劣化数据指标:当限制在 150MB 并且无法下载的时候,对用户的新增有 10%的影响。由于 iOS13 限制的宽松化,所以在 iOS13 之后设备上这个数据将低于 10%。此数据仅供参考并不能一概而论,对于不同类型的 App 首次安装的场景会呈现差异,比如生活服务、出行类 App 对应蜂窝下载场景会多于影音类、游戏类 App。
其次对仍然需要支持 iOS8 以下的 App, 超出 __TEXT 段大小的限制将会很大程度上影响审核以及发版进度。当然可以通过一些手段进行救急,比如拆分动态库的方式绕过。但是这些手段可能导致安装包整体变得更大。
除了 Apple 的限制外,包大小的劣化一定程度上意味着更加慢的启动速度;更多的的代码逻辑;更低研发效率;过于复杂的代码还会带来对代码修改的风险将对稳定性产生负面影响;让性能等基础体验变差,所以包大小不是一个孤立的指标,它从侧面的反映出 App 的健康状态。
安装包的构成以及如何分析安装包
安装包构成
将 ipa 安装包后缀名改为 zip,将其解压,显示.app 包内容后,就可以很直观的看到安装包的组成部分。一般会包括以下几个部分:
- Exectutable: Mach-O 可执行文件
- Resources:资源文件
- 图片资源:Assets.car/bundle/png/jpg 等
- 视频 / 音频资源:mp4/mp3 等
- 静态网页资源:html/css/js 等
- 视图资源:xib/storyboard 等
- 国际化资源:xxx.lproj 其他:文本 / 字体
- / 证书 等
- Framework:项目中使用的动态库
- SwiftSupport: libSwiftxxx 等一系列 Swift 库
- 其他依赖库:Embeded Framework
- Pulgins:Application Extensions
- appex:其组成大致与 ipa 包组成一致
安装包分析
通过分析安装包,了解安装包中可执行文件占用大小、资源占用大小,了解安装包的现状。明确从哪里入手可以获得 ROI 最高的优化手段。而在做包大小分析过程中比较难的是,怎么样通过线下的安装包衡量对下载大小的影响。
但由于上传到 AppStore Connect 到之后,Apple 对安装包做了一些调整,线下安装包的变化无法对应到真正的下载大小变化的变更。而这部分调整包括:
- App Slicing 对于不同架构的裁剪,可执行文件只剩下单架构;
- Asset.car 中图片只留下设备需要的特定尺寸和压缩算法的变体;
- 二进制部分__TEXT 段的通过 FirePlay 进行加密导致 __TEXT 段的压缩比为 1( iOS 13+
以上设备下载变体中苹果移除了这个加密 );
所以线下评估的时候,通过删除 Asset.car 中图片带来 10MB 的包大小的减小,但对最后的下载大小影响可能远远小于 10MB 。而当增加的 2MB 的代码 ,最后的下载大小实打实地增长了 2MB。
可执行文件分析
安装包中的可执行文件,占了安装包中很大一部分空间,而这部分不光和代码有关还和编译、链接过程中添加的参数,编译的机器环境、Xcode 版本等等都有关系。而常常通过 LinkMap 来对可执行文件进行分析。
LinkMap 中包含了可执行文件的架构信息,段表,链接了的所有文件,以及文件中各符号占用的大小。其实通过分析 LinkMap 就基本得到了可执行文件中包含了哪些东西。这部分数据也有助于针对性的进行一些优化。
常见的包大小优化手段
资源文件瘦身
去除无用 / 重复的资源
业务的迭代开发,出现无用的图片资源是比较正常的,我们可以借助工具找出哪些图片资源没有被使用过。推荐下面两款工具:
- LSUnusedResources:可视化客户端工具;
- FengNiao:命令行工具,可嵌入到Run Script中或者在CI系统中使用,支持的模式匹配更加强大。
因为这类工具的原理都是在相关文件(.m、.swift 等等)中利用正则表达式检测是否有图片名称的字符,所以存在以下问题。 问题点:
- 如果代码中使用的图标名称是拼接而成的,就会误以为相关图片是废弃图片;
可以利用Duplicate Photos从内容上检测重复/相似图片。
之所以要使用自动化工具来检测重复资源的原因是因为资源是弱类型,我们在项目迭代过程中手动去维护是相当麻烦的一个过程。转换一下思维,如果资源变成强类型了,那我们维护起来就相当容易了。目前就有这样一个工具 R.swift一定意义上将资源变成强类型,类似于 Android 开发中的 R 文件。
- 可利用fdupes查找项目中的重复文件。其原理是对比不同文件的签名,签名相同的文件就会判定为重复资源。
mac 上可直接通过 brew install fdupes 进行安装,可以使用 fdupes -Sr 文件夹名称 来查看所有涉及到的目录和子目录中的重复文件的大小,其余相关指令可自行查阅,不建议使用 fdupes 相关命令直接删除搜索出来的重复资源,风险比较高。
结论:
考虑到工具的不准确性,可以利用工具粗检测一下哪些资源没有被使用,然后经人工确认后才统一进行删除。对于工具无法检测出来的资源,就只能人工进行筛查了,可每人分配几个模块,提高效率。
资源压缩
请注意:这里的资源不包括 Assets Catalog 管理的资源。
PNG 资源
Xcode 的 Build Setting 提供的给我们两个编译选项来帮助压缩 PNG 资源。
- Remove Text Medadata From PNG Files(默认开启):能帮助我们移除 PNG 资源的文本字符,比如图像名称、作者、版权、创作时间、注释等信息。
- Compress PNG Files(默认开启):当设置为 YES 后,打包的时候会利用 pngcrush 工具自动对项目中所有 PNG
图片进行无损压缩以及修改文件格式,该工具是开源的–pngcrush地址。
Compress PNG Files 设置为 YES 后,XCode 会调用该路径的脚本
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/iphoneos-optimize。
pngcrush 工具在其同级目录存放,iphoneos-optimize 脚本中关于 PNG 压缩的内容如下:
sub optimizePNGs {
my $name = $File::Find::name;
if ( -f $name && $name =~ /^(.*)\.png$/i) {
my $crushedname = "$1-pngcrush.png";
// $PNGCRUSH便是pngcrush工具路径
my @args = <