优化安装包大小

前言

周末跟朋友交流 app 新功能时,发现还没更新,于是打开 app store 准备更新,发现由于包太大了(下载大于 150M),无法使用蜂窝数据下载,周围又没有 WIFI,非常尴尬。这件事让我意识到包大小对 app 运营的影响。本篇文章基于这次优化安装包大小的实践,整理了几个的可行方案,并将图片的优化做了自动化脚本 AppThinning

AppThinning

IPA 安装包构成

想要优化安装包,最直接的想法就是分析下安装包的构成,做针对性的优化。

image

整理归类后大致如下:

  • 可执行文件(Mach-O)
  • 资源文件
    • 图片:png、jpg、bundle、Assets.car 等
    • xib、storyboard
    • 其他资源:音视频、文本、网页等
  • Framework

App Thinning

了解完安装包构成,接下来我们应该找些方法来解决它。下面先介绍下苹果的方案 App Thinning。

App Thinning 是 iOS 9 之后引入的一项优化,主要包括三项功能:Slicing、Bitcode、On-Demand Resources。

Slicing

image

根据苹果官方文献的描述「Slicing 是为应用捆绑包创建、分发不同变体以适应不同目标设备的过程。一个变体只包含针对某个目标设备的可执行架构与资源。」 换句话说,App Slicing 仅向设备传送与之相关的资源(取决于屏幕分辨率,架构等等),具体如下图所示。

image

需要注意的是,图片资源需要放在 Asset Catalog 中才能实现 App Slicing

Bitcode

Bitcode 是一种程序中间码。包含 Bitcode 配置的程序将会在 App store 上被编译和链接。Bitcode 使用最新的编译器自动编译 app 并且针对特定架构进行优化。Bitcode 不会下载应用针对不同架构的优化,而仅下载与特定设备相关的优化,同时与前文所述的 App Slicing 配合实现,使得下载量更小。这部分都是在服务端自动完成的,所以假如以后 Apple 推出了新的 CPU 架构或者以后 LLVM 推出了一系列优化,我们也不再需要为其发布新的安装包了,Apple Store 会为我们自动完成这步,然后提供对应的 variant 给具体设备。

image

需要注意的是,开启 Bitcode 需要全部支持,包括依赖的静态库、动态库。

On Demand Resources

On-Demand Resource,就是将一部分资源放置在苹果的服务器上,不随着 App 的下载而下载,直到用户真正进入到某个页面时才下载这些资源文件。这种模式非常适合游戏的解锁关卡

image
image

关于 On Demand Resources,目前还未实践过,这里不深入介绍,感兴趣的可以查看苹果的文档。

包实际大小

当我们上传包到 App Store Connect 后,需要等到 completed processing 后才能查看 app。而这段时间,苹果服务器帮我们做 App Thinning 的一些操作,生成各类设备对应的包。在活动-》所有构建版本中可以看到实际的下载和安装大小。

image

优化方案

优化包大小问题,我们很自然能想到的几个方案是:

  • ”删”:删除无用文件
  • “压”:压缩大文件
  • “改”:改变资源获取方式、存储方式等

图片资源

图片资源占用的空间是最大的, 应优先考虑处理图片资源。

“删”

FengNiao

image

LSUnusedResources

image

需要注意的是,这类工具存在一点问题,会出现误报,不过可以作为参考,帮助找出无用文件。

“压”

压缩工具

ImageOptim

image

ImageOptim 支持 PNG/JPEG/GIF 动画,本质是各种影像优化工具的图形前端:AdvPNG、OptiPNG、Pngcrush、JpegOptim、jpegtran、Gifsicle 和 PNGOUT 素材。

  • 优点:无损、 小巧
  • 缺点:压缩比一般、压缩速度一般

另外 ImageOptim 提供了命令行工具 ImageOptim-CLI

tinypng

在线图片压缩平台,特点是速度快,压缩比高。

image
  • 优点:压缩比高(正常可达 50% ~ 70%)、压缩速度快
  • 缺点:每次最多只能处理 20 张图片、每张图片不能超过 5MB、图层较多时有可能出现像素损失情况(但概率很低)

另外 tinypng 提供了 api 调用形式,每个月免费 500 张。

经实践,tinypng 的压缩比和速度都较优秀,强烈建议优先使用 tinypng 压缩。当 tinypng 不能满足要求时,可以选择 ImageOptim。

Bundle

优先压缩 Bundle 内的图片,因为 Bundle 里面的图片不支持 App Slicing。

Assets.car

安装包中,除了在 Bundle 中的图片,其他的图片主要包含在 Assets.car 中。
Assets.car 是 Assets.xcassets 经过编译后的文件。通过分析 Assets.car 文件,可以帮助我们找出大文件。

AssetCatalogTinkerer

[图片上传失败...(image-d3d4a8-1568800701765)]

AssetCatalogTinkerer 是一款开源的查看和提取 car 文件中的图片工具。

image

导出所有图片后,就可以根据大小排序找到大文件,进行对应的压缩处理。

assetutil

assetutil 是 xcode 自带的命令行工具。使用 assetutil 分析 Assets.car 可以获取到图片更详尽的数据,比如:AssetType、Colorspace、SizeOnDisk 等。

xcrun --sdk iphoneos assetutil --info Assets.car
[
  {
    "AssetStorageVersion" : "IBCocoaTouchImageCatalogTool-10.0",
    "Authoring Tool" : "@(#)PROGRAM:CoreThemeDefinition  PROJECT:CoreThemeDefinition-346.29\n",
    "CoreUIVersion" : 498,
    "DumpToolVersion" : 498.4,
    "Key Format" : [
      "kCRThemeAppearanceName",
      "kCRThemeScaleName",
      "kCRThemeIdiomName",
      "kCRThemeSubtypeName",
      "kCRThemeDeploymentTargetName",
      "kCRThemeIdentifierName",
      "kCRThemeElementName",
      "kCRThemePartName",
      "kCRThemeDimension1Name",
      "kCRThemeDimension2Name"
    ],
    "MainVersion" : "@(#)PROGRAM:CoreUI  PROJECT:CoreUI-498.40.1\n",
    "Platform" : "ios",
    "PlatformVersion" : "9.0",
    "SchemaVersion" : 2,
    "StorageVersion" : 15
  },
  {
    "AssetType" : "Vector",
    "Colorspace" : "srgb",
    "Height" : 500,
    "Idiom" : "universal",
    "Name" : "next-joystick-progress",
    "RenditionName" : "next-joystick-progress",
    "SizeOnDisk" : 9324,
    "Width" : 1246
  },
  {
    "AssetType" : "Vector",
    "Colorspace" : "srgb",
    "Height" : 100,
    "Idiom" : "universal",
    "Name" : "next-success-icon-1",
    "RenditionName" : "next-success-icon",
    "SizeOnDisk" : 4385,
    "Width" : 100
  }
  ...

若图片较多,可导出到文本查看

xcrun --sdk iphoneos assetutil Assets.car -I -o out.text

“改”

图片管理方式

因图片资源需要放在 Asset Catalog 中才能实现 App Slicing。因此,图片尽量使用 Asset Catalog 管理。

图片格式

细心的朋友在分析 Assert.car 的时候会发现,在 Assets.xcassets 放的 pdf 文件在编译后生成了 png 文件放在 Assert.car 中。
有些尺寸较大的 pdf 文件,本身文件大小很小,但是编译生成的 png 文件却很大
下图样式的 pdf 文件,大小为 10K,编译生成后的 png 确是 2M 多。

image

Why I don’t use PDFs for iOS assets 中也讲述了 pdf 的一些坏处。

那 pdf 就一定不好吗?
答案是否定的。苹果之所以支持 pdf,是因为向后兼容的问题。图片使用 pdf 格式,编译后自动生成 1x 和 2x 以及 3x png 图片,若以后屏幕质量提升了,出现了 4x 设备,那使用 pdf 的 app 就会生成 4x 的图自动适配,跟 BitCode 的思想如出一辙。

那何时使用 pdf 何时使用 png 呢?
pdf 生成 png 的规则是,按 pdf 的原始尺寸作为 1x 的大小。因此,尺寸较大的图片不适合使用 pdf 格式
经实践,发现若图片是使用矢量工具直接画出来的图片(可矢量化),且尺寸较小的图片,比较适合 pdf 格式;若图片是拍摄的渲染图则推荐使用 png

存储方式

包内不适合存放大图片,大图尽量使用网络形式获取。

其他资源

“删”

参考处理图片资源的工具,找到无用的资源文件,进行删除。

“压”

网页资源

让前端人员使用 webpack 打包并压缩代码后,再加入 iOS 工程。

音频文件

mp3smaller

视频文件

How To Compress a Video File Without Losing Quality

可执行文件(Mach-O)

这部分的优化,主要包含两部分:

  • 文件优化
  • 编译优化

文件优化

  • 使用 FengNiao 和 LSUnusedResources 查找并删除无用类
  • 删除无用方法、引用
  • 删除重复代码

工具推荐:

  • fui 帮助查找无用引用。
  • PMD 静态代码扫描工具帮助查找重复代码。
    image
  • AppCode 帮助查找无用的类、无用的方法甚至是无用的 import ,但是无法扫描通过字符串拼接方式来创建的类和调用的方法。
    image
  • LinkMap 帮助检查到底是哪个类、哪个第三方库占用了太多空间
    image

这部分的优化非常有限,因为类文件都是文本,对于包的体积帮助很小,但是对于 Clean Code 却显得很重要,这里就不再赘述。

编译优化

  • Architectures
image

若项目不需要再支持 32 位,可以去除 armv7,这样可执行文件以及库的大小,必然会大大减小。

  • Generate Debug Symbols

Generate Debug Symbols 决定是否生成调试符号。开启时会生成 symbols 文件,关闭时 ipa 中不会生成 symbol 文件,可以减少 ipa 大小,但会影响到崩溃的定位。

推荐:保持默认开启或者 debug 关闭、release 开启

  • Swift Compiler - Code Generation

    • Optimization Level
image
分为 6 个级别:

- None[-O1]:代码没有优化,编译时间最快。
- Fast[-O,O1]:适度优化,没有显著的降低编译时间,在编译过程中使用更多的内存。
- Faster[-O2]:几乎全面优化,生成高度优化的代码,编译器不执行循环展开或函数内联,编译时间较慢。
- Fastest[-O3]:全面优化,编译器执行内联函数。**这个选项通常不推荐。有关更多信息,请参见[避免过度内联函数](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/CodeFootprint/Articles/CompilerOptions.html#//apple_ref/doc/uid/20001861-131770)**。
- Fastest,Smallest[-Os]:全面优化,优化程序的空间使用,通常不增加代码大小,并且更小的内存占用。
- Fastest,Aggressive Optimizations[-Ofast]:苹果文档上无详细说明。

详见[苹果文档](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/CodeFootprint/Articles/CompilerOptions.html#//apple_ref/doc/uid/20001861-CJBJFIDD)

推荐:debug 模式下选择 None 保证编译速度;release 模式下选择 Fastest,Smallest[-Os]。

  • Compilation Mode
image
有两个选项:

- Incremental:逐个文件进行优化,它的好处是对于**增量编译的项目来说,它可以减少编译时间**,对没有更改的源文件,不用每次都重新编译。并且可以充分利用多核 CPU,并行优化多个文件,提高编译速度。但它的缺点就是对于一些需要**跨文件的优化操作,它没办法处理**。如果某个文件被多次引用,那么对这些引用方文件进行优化的时候,会反复的重新处理这个被引用的文件,如果你项目中类似的交叉引用比较多,就会影响性能。
- Whole Module:将项目所有的文件看做一个整体,不会产生 Incremental 模式对同一个文件反复处理的问题,并且可以进行最大限度的优化,包括跨文件的优化操作。缺点是,不能充分利用多核处理器的性能,并且对于增量编译,每次也都需要重新编译整个项目。

推荐:debug 模式下选择 Incremental;release 模式下选择 Whole Module。

其他选项,大家可以参考苹果文档的 Managing Code Size 章节。

Framework

image

在 Framework 中可以看到系统的 dylib 和第三方的 framework。这部分可以根据项目的功能,查找未使用的库并删除。
如果使用了 Swift,则项目会加入 swift 的 dylib。关于这点,有些 iOSer 放弃了 swift 回退到了 oc。个人觉得这个选择并不明智,swift 有许多优秀的特性,如果仅仅因为 swift 库的大小而转向 oc 太不值当了。

自动化

基于本次实践,本人写了一个脚本 AppThinning,帮助自动找到大文件,然后进行图片压缩(imageOptim、TinyPng 可选),目前只支持图片文件的压缩,后续会加上更多类型的压缩,以将安装包大小的问题自动化。当然,更推荐大家将脚本放到持续集成中,当成集成的一部分

总结

至此,常见的优化安装包大小的方法都已介绍完毕。大家可以根据自己项目的特点,合理选择。从本次优化的实践,个人觉得,首先应该优化图片资源,这是最简单,也是最有效的。其次,删除无用的文件、方法等不但可以减少体积,而且可以让代码变得更 Clean。持续集成中有一句老话,“如果你遇到一件很痛苦的事情,似乎比较好的建议就是更频繁地做这件事情”。所以我们不妨把 Clean Code 作为习惯,这样代码永远都是干净的,也就不需要想着什么时候优化大小的问题。最后一点,不要做一个只会码代码的程序员,学点产品思维,或许砍需求是最有效的优化安装包大小的方法

感谢

iOS 瘦包,常见方式梳理
干货|今日头条iOS端安装包大小优化—思路与实践

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,513评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,312评论 1 305
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,124评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,529评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,937评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,913评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,084评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,816评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,593评论 1 249
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,788评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,267评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,601评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,265评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,158评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,953评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,066评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,852评论 2 277

推荐阅读更多精彩内容