记录 IPA 包体积优化过程中的一些思路。

背景

随着业务量的不断增加,应用安装包大小也在迅速的增长,而安装包大小的增加对应用的推广拉新会造成比较大的影响。因为这样,我们进行了 2 期的安装包大小的优化,第一期优化从 107MB 到 84MB,第二期优化 8MB。本篇主要是用于记录优化过程中的一些思路。

优化方向

优化前我们首先要确定的是优化指标优化方向。我们最终是以 ipa 包中 xxx.app 大小作为优化前后的对比项。优化方向上,首先我们需要看下 ipa 包中 xxx.app 有哪些组成。

解压 xxx.app,会看到的模块有:

  • AppIcons。各尺寸的应用图标。
  • Assets.car。Asset Catalog 的编译产物。
  • Frameworks。存放着动态库。
  • 编译后的可执行文件
  • PlugIns。插件路径(如 Today Extension)
  • 其他

通过文件结构,我们大概有的优化思路是:

  • 资源文件(图片或配置文件)的处理
  • 代码的处理,以减小编译后可执行文件大小
  • Today Extension 的代码优化
  • 编译配置是否有可优化的点

根据这 4 个优化方向,具体看下对应的优化方案。

优化方案

资源文件优化

图片资源

关于图片,我们使用的也是通用的方案,主要是两方面的处理:

  • 无用图片的移除
  • 图片压缩

项目从 3.8.0 版本开始,图片资源除去启动图及部分场景下使用的 AppIcon,其他图片全部迁移到了 Images.xcassets 中,所以针对 Images.xcassets 进行图片处理。

  1. 无用图片的移除

    • 通过资源关键字进行全局匹配,筛选出未使用的资源文件
    • 这里我们使用的是 LSUnusedResources
    • 查找出图片后,需要人工再对图片的使用情况做确定。之后对应删除
  2. 图片压缩

    • 经过上一步的无用图移除,剩下的图片是当前版本所需要的,可以对这些图片进行无损压缩
    • 因为 TinyPNG有限制,最终我们使用的是批量处理工具 iSparta

比较惊讶的是,我们查找到了 15869.44KB(15MB)的无用图片及压缩了 6MB。

查找重复文件

我们通过 fdupes 工具来扫描查找重复文件。fdupes 是通过校验所有资源的 MD5,筛选出项目的重复资源。文件比较顺序依次为:

  • 大小对比
  • 部分 MD5 签名对比
  • 完整 MD5 签名对比
  • 逐字节对比

相关命令:

# 安装
brew install fdupes

# 查看某文件夹下的重复文件
fdupes -Sr ./filepath > outlog.txt

大文件转下载

非必要的大文件资源,如字体库、皮肤资源、子页面大图,不放到 ipa 包中,转为下载的方式。

关于 Images.xcassets

iOS9 开始苹果建议将图片资源文件放入 Images.xcassets 中。Images.xcassets 中的图片在加载后会有缓存,可以提升加载速度;打包时会进行自动的 PNG 图片压缩,再根据运行设备的不同分发 x2/x3 图片资源。

Pod 库也可以通过 Images.xcassets 来存放图片资源,在 podspec 中指定 resource_bundles:

s.resource_bundles = {
'podname' => ['./Assets/*.xcassets']
}

相关的编译配置:

  • Compress PNG Files
  • Remove Text Metadata From PNG Files

代码层面优化

按责分配模块,review 代码,去除重复代码。也是工作量比较大的一个模块。

编译选项配置

  1. 指定编译生成包所支持的架构
    Build Settings -> Valid Architectures 中,因为不再支持 iOS9 以下及 32位,我们去掉了对 armv7 的支持,以减小 ipa 包大小。

  2. 编译器优化级别
    Build Settings -> Optimization Level 在 release 版选择 Fastest, Smallest。会优化可执行文件的大小,使其尽可能小。

  3. 去除符号信息
    Build SettingsStrip Linked Product / Deployment Postprocessing 在 release 版本设置为 YES,可以去除不必要的调试符号。
    Strip Linked Product 默认为 YES,Deployment Postprocessing 默认为 NO,而 Strip Linked Product 在 Deployment Postprocessing 设置为 YES 的时候才生效。

这些配置选项的在旧版 Xcode 生成的项目中不一定默认遵循,可以检查下,以达到一定的编译优化。

App Thinning

iOS9 开始引入了 App Thinning,相关 Session 介绍:WWDC15 - App Thinning in Xcode

App Thinning 的概念是,当用户安装应用时,苹果会根据当前用户的机型来选择合适的资源文件及对应 CPU 架构的二进制可执行文件(即不会同时存在 armv7、arm64 的情况),减少了用户的下载流量和安装占用的空间。

App Thinning 的开启方式是,在上传 App 时,配置 App Thinning 即可。

App Thinning 分为三部分:

  • Slicing
  • Bitcode
  • On-Demand Resources

Slicing

原理入图所示。在我们将 Archive 的 App Record 传到 iTunes Connect 后,它会被分割为不同的变体,不同的点是CPU 架构的二进制可执行文件(如 armv7 或 arm64)及 资源文件(如 Image.xcassets 中 x2/x3 的图片)。

当用户从 App Store 下载时,只会下载特定的变体。

Bitcode

Bitcode 是编译好的程序中间码,在苹果推出新的架构或有新的 LLVM 优化时,不需要重新上传 App。

开启方式是,Build Settings -> Enable Bitcode 置为 YES 即可,需要 App 中所有的静态库和动态库都支持,避免编译失败。

On-Demand Resources

按需加载资源。在 Build Settings 中需要开启 Enable On Demand Resource,接着在 Resource Tags 中添加对应的资源文件,使用时通过 NSBundleResourceRequest 获取按需加载的资源文件。

更多详细介绍:WWDC15 - Introducing On Demand Resources

其他方向

  • Framework 只保留需要的指令集。可以通过 lipo 命令处理。
  • App Extension 中使用动态库,以和主 App 共享同一个库。
  • 第三方库在引入时慎重,尽量只引入需要的模块。

总结

主要是从资源文件、code review 及编译选项配置几个方面,进行 App 的瘦身。过程中要利用苹果自身的机制,如 Image.xcassets、App Thinning 机制。

iOS 项目在开发初期,我们很容易忽略安装包大小的增长,除去后期的优化,在日常开发中也需要关注的一些点。

  • 资源文件在引入时,需要按需做无损压缩和有损压缩
  • 不要为了一个小功能而引入一个大的库
  • 平时开发过程中,废弃模块要及时清理
  • 同类开源库,只引入一个
  • 最好能够有预警机制,打完包后,能够生产一份分析文件,列出与上次包的大小对比

评论