iOS打包速度优化

写这篇文章的契机是在开发过程中,我发现车e估日常测试打包只需要不到十分钟,而车商则经常大于16分钟,所以就想通过一些方法来减少打包时间。

在讨论打包优化方法之前,我们先了解一下编译过程。iOS编译的过程可以简化为:预处理-编译生成中间代码-汇编器生成汇编代码-生成机器码-链接-生成可执行文件。在我们打包的时候,可以简单的认为打包时间由:编译时间+链接时间+生成调试信息时间。由于链接是没有缓存的,而且只能用单核进行,所以它的耗时主要取决于单核性能和磁盘读写速度,所以这一块除了更换设备,没有什么优化空间。对于调试信息,我们日常测试包可以选择,不生成调试信息(dSYM文件),这样可以减少20秒左右的时间。可以看到,我们可以减少的时间主要放在编译阶段。

1 减少编译文件和资源

由于在编译过程中,所有依赖的文件、系统库、第三方库都会被引入编译,因此一个比较直接的方式就是减少文件和资源的数量,保持代码整洁,对于不需要使用的库和文件,及时进行清理。比如我们这次车e估开发过程中,有一组准备废弃的相机基类,在确定不使用且通过测试之后就删除了此次开发过程中最开始继承该基类的文件,

下图是车商的部分依赖系统库,可以看到还有Twitter这种,所以文件在日积月累的情况下,是会有不少冗余依赖的。

image.png

2 减少重复编译

现有项目中,我们的引入依赖文件都是使用#import。由于#import 的本质就是把被依赖头文件的内容拷贝到自己的头文件中来,因此头文件 A 中实际上包含了 M N 个头文件的内容,也就需要 M N 次文件 IO 和相关处理。当项目中每增加一个依赖头文件 A 的文件,就会重复一次上述的 M * N 复杂度的过程。

以前我们会使用PCH文件。PCH 文件的好处是,这个文件中的头文件只会被编译一次并缓存下来,然后添加到项目中所有的头文件中去。上述问题倒是解决了,但很智障的一点是,所有文件都会隐式的依赖所有 PCH 中的文件,而真正需要被全局依赖的文件其实非常少。因此实际开发中,更多的人会把 PCH 当成一种快速 import 的手段,而非编译性能的优化。前文解释过,PCH 文件一旦发生修改,会导致彻彻底底,完完整整的项目重编译,从而降低编译速度。正是因为 PCH 的副作用甚至抵消了它带来的优化,苹果已经默认不使用 PCH 文件了。

用来取代 PCH 的就是 Clang modules 技术,对于开启了这一选项的项目,我们可以用 @import 来替代过去的 #import,比如:
@import UIKit;等价于 #import <UIKit/UIKit.h>。抛开自动链接 framework 这些小特性不谈,Clang modules 可以理解为模块化的 PCH,它具备了 PCH 可以缓存头文件的优点,同时提供了更细粒度的引用。

还有一个小技巧是使用向前声明。在.h文件中使用@class CLASSNAME,而不是#import CLASSNAME.h。将引入头文件的时机尽量延后,只在需要的时候才引入,这样可以减少类使用者所需要引入头文件的数量。而且前向声明也解决了两个类的循环引用问题。

对常用的工具类进行打包(Framework/.a)打包成Framework或者静态库,这样编译的时候这部分代码就不需要重新编译了。在车商跟车e估打包的对比过程中我发现,车商的Realm这个第三方库,每一次编译会花费很长时间,而车e估则没有这个花费,原来是车e估没有使用cocoapods来管理Realm,而是手工引入realm的framework,所以花费时间大大减少。对于稳定的第三方库,或者我们编写的工具,我们也可以采用这种方式来降低打包花费。
由此其实引发了我的一点思考,对于iOS开发来说,我们一般用cocoapods来管理第三方库,但是这个管理意义是什么呢?我觉得最重要是为了管理各种依赖配置,不用再去手动配置,其次是为了保持第三方库的优化、更新和bug修复。在我们的项目中cocoapods其实只是用了他的第一项功能,那么我的想法就是可以对一部分编译很耗时的第三方库手工引入和管理依赖,这个其实不用花太多功夫,但是可以为以后的每一次打包节省不少时间。

3 缓存编译

其实如果我们每次打包之前不clean的话,我们会发现打包时间其实会缩短很多的。这是因为Xcode本身使用了增量编译的方式,每次编译会重新编译修改文件和修改文件的引用,但是对于没有改动的部分则会使用之前缓存的编译结果。但是这个增量编译并不稳定,很久之前在打包过程中出现过增量编译没有打包最新代码的问题。现在的增量编译应该是越来越稳定的,但是个人对于这个稳定问题的担心仍然存在。

因此出现了一些第三方缓存工具,比较容易接入的是ccache这个工具。ccache是一个能够把编译的中间产物缓存起来的工具,而且比较稳定,因此可以大大提高编译速度。但是ccache不支持clang module,因此需要关闭项目中的Enable Modules,同时对于cocoapods管理的第三方库,也需要处理clang module的问题。具体使用方法可以参考文章使用ccache让打包飞起来。 ps:我没有配置成功,在配置过程中找不到对应的编译器。但是根据其他程序员的实践,这种缓存编译的方式可以提升五倍以上的打包时间,以后有时间再继续研究一下。

4 修改工程设置

一般来说,我们的持续集成工具主要是用来给产品经理或者测试人员使用,用来体验功能或者验证 Bug,除非是需要上架 App Store,否则并不需要关心运行时性能。然而在手机上使用的 Release 模式,默认会开启各种优化,这些优化都是牺牲编译性能,换取运行时速度,对于上架的包而言无可厚非,但对于那些 Daily Build 包来说,就显得得不偿失了。

因此,加速打包的思路和优化的思路是完全互逆的,我们要做的就是关闭一切可能的优化。

Valid Architectures
这个选项是指定处理器的指令集。对于功能测试来说,我们可以指定只打包测试机对应的指令集。

armv7|armv7s|arm64|arm64e都是ARM处理器的指令集
i386|x86_64 是Mac处理器的指令集

指令集对应的机型:
2018 A12芯片arm64e : iphone XS、 iphone XS Max、 iphoneXR
2017 A11芯片arm64: iPhone 8, iPhone 8 Plus, and iPhone X
2016 A10芯片arm64:iPhone 7 , 7 Plus, iPad (2018)
2015 A9芯片arm64: iPhone 6S , 6S Plus
2014 A8芯片arm64: iPhone 6 , iPhone 6 Plus
2013 A7芯片arm64: iPhone 5S
armv7s:iPhone5|iPhone5C|iPad4(iPad with Retina Display)
armv7:iPhone4|iPhone4S|iPad|iPad2|iPad3(The New iPad)|iPad mini|iPod Touch 3G|iPod Touch4

模拟器32位处理器测试需要i386架构,
模拟器64位处理器测试需要x86_64架构,
真机32位处理器需要armv7,或者armv7s架构,
真机64位处理器需要arm64架构。

Optimization Level
关闭编译优化。优化的基本原理是牺牲编译时性能,追求运行时性能。常见的优化有编译时删除无用代码,保留调试信息,函数内联等等。因此提升打包速度的秘诀就是反其道而行之,牺牲运行时性能来换取编译时性能。对于日常测试打包,可以将Optimize level 的Release状态下的值改成 O0,表示不做任何优化。

Debug Information Format
不生成 dYSM 文件,Release状态下的值修改为DWARF。

5 采用新构建系统(New Build System)

苹果从Xcode 9开始推出了新构建系统(New Build System),并在Xcode 10使用其为默认构建系统来替代旧构建系统(Legacy Build System)。采用新构建系统能够减少构建时间。

简要介绍一下原理,对于旧构建系统,当我们构建一个程序的时候,会明确所需要构建的所有Target,这些Target之间的依赖关系,以及这些Target构建的顺序。采用顺序会造成多处理器系统资源的浪费,从而表现为编译时间的浪费,解决这个问题的方式就是采用并行编译,这也是新构建系统优化的核心思想。详细了解新构建系统,探究Xcode New Build System对于构建速度的提升。

6 使用脚本

总结

上面就是总结的一些提高打包速度的一些方法,应该根据项目实际情况选择合适的方法优化,对于中小型尽量将打包时间控制在十分钟以内。对于项目改动较小且比较容易实现的方式主要是:减少编译文件和资源、 减少重复编译以及工程配置修改这几种方式。

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

推荐阅读更多精彩内容