iOS IPA体积优化攻略,从可执行文件、资源、三方库、打包配置中排查

0 阅读5分钟

同事在群里发 说 TestFlight 里我们那个 App 显示"大小 280MB",后面跟一句"这个正常吗"。功能做得不算多,体积怎么就上去了?花半天看,大头不在业务代码——是几处从立项起就没人动过的构建配置,加上两个功能重叠的第三方库。这类问题问的人多,真去拆开看过体积构成的人少——好在包体积属于少数能"拆开数"的东西,量出来再动手,比拍脑袋稳。

这篇按"体积从哪来"分四类排查,每一类给出确认方法和瘦身手段。思路都是先测、再改、改完重新出包对比。

可执行文件为什么这么大?

IPA 解压之后,里面有个和 App 同名的可执行文件(Mach-O 格式),它经常是体积的大头。在 macOS 上两条命令能先看个大概:

size -m MyApp.app/MyApp      # 看各段大小:__TEXT 是代码,__DATA 是数据
lipo -info MyApp.app/MyApp   # 看包里有哪些 CPU 架构

__TEXT 段明显偏大的话,逐个查这几处:

  • 优化等级:Release 配置用 -Os(按体积优化);用 -O(按速度优化)会大一圈,这俩换来换去就差在这里。
  • LTO:链接期优化,跨模块把冗余代码裁掉,Xcode 里对应 Link-Time Optimization,Release 建议打开。
  • 符号裁剪:Strip Linked Product 和 Deployment Postprocessing 在 Release 下都设成 YES,调试符号不进包。
  • 死代码裁剪:Dead Code Stripping 在 Release 下默认开着,没人调用的函数链接时被丢掉——工程配置要是被人动过,翻出来确认它还在。
  • 架构:lipo -info 如果列出了 x86_64,说明模拟器版本混进上架包里了——正常的上架包只含 arm64。

还有一条 Swift 项目的额外账:泛型用得多,编译器会为不同具体类型生成多份特化代码,__TEXT 会比同功能的 Objective-C 项目大一些。这是语言特性带来的,先把上面几条配置层的收益吃干净,再考虑代码层怎么收拾。

资源文件怎么占了这么多?

资源的问题在于"隐形":图片、音视频、字体摊在包里,没谁逐个统计过。

  • 切片:图片放进 Asset Catalog,出包时按机型做 App Thinning,不同设备只下发自己需要的倍率。放在 bundle 里当散文件用的图不享受这个待遇,@2x、@3x 全量跟着包走。
  • 格式:大图换 HEIC 或 WebP,同画质下比 PNG 小不少。
  • 字体:中文字体是常见的隐形大户,一个字重十几 MB 不稀奇,只用得到一两档字重的话,其余的别打进包。
  • 音视频:演示视频、示例音频这类文件一放就是几十 MB,非必要的不放主包,或者走按需资源(On-Demand Resources),用户用到时再下载。
  • 清理:老项目里沉积的废弃图片能占出几十 MB,但动它们之前先看下面的警告。

按名字动态加载的资源不能乱删:UIImage(named:)、按路径从 Bundle 读的音视频、storyboard/xib 里引用的文件,编译器看不到这类"按字符串找文件"的引用。删掉不会报错,运行时才崩。

第三方库和 Flutter 引擎能砍吗?

  • 同功能多库并存:项目大了,常同时躺着两个网络库、两个图片加载库——不同时期不同人引入的。依赖清单用 otool -L 或者 dyld_info -dependents 拉:对主可执行文件和几个大框架各跑一遍,链接和加载了哪些库一目了然,功能重叠的合并掉。
  • 动态框架的代价:每嵌入一个动态 framework,包里就多一份独立二进制和一套签名,数量多时改成静态链接能省一截。
  • Flutter 项目的下限:体积由 Flutter 引擎决定,引擎二进制是固定的大头,业务代码再怎么优化也压不下去。用 Flutter 得有个心理预期——包天生比纯原生大,能优化的是引擎之外的部分。KXApp 支持 Flutter 项目类型,编译出包走同一套流程,引擎的体积构成一样放在这里看。

打包配置漏了哪几个开关?

  • 出包用的是不是 Release 配置:这条是新手最常踩的——Debug 配置带着调试信息和不优化代码,体积差一大截。
  • App Thinning Size Report:Archive 后导出时勾上 all compatible device variants,能得到一份按机型的体积报告,切片生效没有,报告里写得清楚。
  • bitcode:老方案,苹果前几年停用了 bitcode 上传,"开 bitcode 减体积"的教程可以不用翻了。
  • 下载限额:App Store 的蜂窝下载有个体积限额,超了用户没连 Wi-Fi 就会被拦一道。上架前瞄一眼 App Store Connect 里的"App 大小"页,那里显示的是按机型分发之后的实际下载体积。

改完怎么确认瘦了?

改配置、换资源、合依赖之后,判断标准就一个:重新出一版包,跟旧的比。走 Xcode 的话看 Archive 导出的体积报告;用命令行发布的话,Fastlane 的 gym 出包后直接对比 IPA 大小。

不挂在 Xcode 工具链上的环境也是同一条路。KXApp 这类一键构建出包的工作流里,配置改完重新构建一版,出包结果摆在面前——效果不看配置文件,看包。

对比时留意口径:IPA 文件大小、商店里显示的下载大小、装机后的实际占用,这三个数不相等,前后用同一个口径量,不然容易自己吓自己。

有 CI 的话,把体积对比固化进流水线更省心:每次构建把 IPA 大小和体积报告归档下来,涨了自动提示,比发版前临时抱佛脚主动得多。

体积优化不是一次性的压缩动作,是构建配置一项项安排的过程——看出哪一类占了大头,挑对应手段,改完重新出包验证。

排查顺序也有个省事的走法:如果体积是某次发版突然涨上去的,先对比相邻两个版本的体积报告,改动集中在哪一块一望便知;如果是慢慢涨上来的,再按上面四类从头过。