OC项目编译优化

参考文章链接:关于Xcode编译性能优化的研究工作总结

一、编译时长优化Architectures

在Build Setting 中,有个Architectures配置选项。

Architectures: 是指定工程支持的指令集的集合,如果设置多个architecture,则生成的二进制包会包含多个指令集代码,提及会随之变大。
Valid Architectures: 有效的指令集集合,Architectures与Valid Architectures的交集来确定最终的数据包含的指令集代码。
Build Active Architecture Only :指定是否只对当前连接设备所支持的指令集编译,默认Debug的时候设置为YES,Release的时候设为NO。Debug设置为YES时只编译当前的architecture版本,生成的包只包含当前连接设备的指令集代码;设置为NO时,则生成的包包含所有的指令集代码(上述的Valid Architecture与Architectures的交集)。所以为了更快的编译速度,Debug应设为YES,而Release应设为NO。

PS:Debug设置为YES时,如果连接的设备是arm64的(iPhone 5s,iPhone 6(plus)等),则Valid Architecture中必须包含arm64,否则编译会出错。这种模式下编译出来的版本是向下兼容的,即:编译出的armv6版本可在armv7版本上运行。

二、编译时长优化Precompile Prefix Header 预编译头文件

Xcode6以及之后版本默认不适用PCH文件参与项目编译,原因有二:

  • 去掉自动导入的系统框架类库的头文件可以提高源文件的复用性,便于迁移;
  • 一个庞大的Prefix Header 会增加Build耗时

但对于原项目应用了PCH文件的情况,就需要对Xcode的Build Setting进行配置以使用pch。

当Precompile Prefix Header设为NO时,头文件pch不会被预编译,而是在每个用到它导入的框架类库中编译一次。每个引用了pch内容的.m文件都要编译一次pch,这会降低项目的编译速度。

将Precompile Prefix Header设为YES时,pch文件会被预编译,预编译后的pch会被缓存起来,从而提高编译速度。 需要编译的pch文件在Prefix Header中注册即可。

三、加载RAM磁盘编译Xcode项目

DerivedData

Xcode会在文件系统中几种缓存临时信息。

每次对Xcode iOS项目进行clean、build或者在iOS虚拟机上launch,Xcode都会在DeriveData文件夹中进行读写操作。换句话说,就是将Derived Data的读写从硬盘移动到内存中。

DeriveData文件夹中包含了所有的build信息、debug- 和 release- built targets以及项目的索引。当遇到零散索引(odd index)问题(代码块补全工作不正常、经常性的重建索引、或者运行项目缓慢)时,它可以有效地删除衍生数据。删除这个文件夹将会导致所有Xcode上的项目信息遭到破坏。

步骤1

将DeriveData下的文件删除:

hdid -nomount ram://4194304  ```

删除这些数据,Xcode会在Build时重新写入

*步骤2*

在~/Library/Developer/Xcode/DerivedData.上部署安装2 GB大小的RAM磁盘。 进到
~/Library/Developer/Xcode/DerivedData.

` cd ~/Library/Developer/Xcode/DerivedData`

创建2 GB的RAM磁盘(size的计算公式 size = 需要分配的空间(M) * 1024 * 1024 / 512):` hdid -nomount ram://4194304 `

此行命令后将会输出RAM磁盘的驱动名字:/dev/diskN(N为数字)。

初始化磁盘:`newfs_hfs -v DerivedData /dev/rdiskN`

有以下输出:    Initialized /dev/rdisk3 as a 2 GB case-insensitive HFS Plus volume

安装磁盘:`diskutil mount -mountPoint ~/Library/Developer/Xcode/DerivedData /dev/diskN `

这会在已存在的DeriveData上安装一个卷,用于隐藏旧的文件。这些文件仍会占据空间,但在移除RAM磁盘之前都无法访问。
在重启或从Finder中弹出RAM磁盘时,磁盘内容会消失。下次再创建磁盘时,Xcode将会重新构建它的索引和你的项目中的中间文件。
创建虚拟磁盘后,并不直接占用掉所有的分配空间,而是根据虚拟磁盘中的文件总大小来逐渐占用内存。
注意:如果虚拟磁盘已满,会导致编译的失败。此时清除掉DeriveData后zhong重新编译,就算有足够的空间也还是有可能会导致编译失败。重启Xcode可以解决此问题。

### 四、编译线程数和Debug Information Format

- **4.1、 提高XCode编译时使用的线程数**

`defaults write com.apple.Xcode PBXNumberOfParallelBuildSubtasks 8 `
其后的数字为指定的编译线程数。XCode默认使用与CPU核数相同的线程来进行编译,但由于编译过程中的IO操作往往比CPU运算要多,因此适当的提升线程数可以在一定程度上加快编译速度。

- **4.2、 将Debug Information Format改为DWARF**
在工程对应Target的Build Settings中,找到Debug Information Format这一项,将Debug时的DWARF with dSYM file改为DWARF。
需要注意的是,将Debug Information Format改为DWARF之后,会导致在Debug窗口无法查看相关类类型的成员变量的值。当需要查看这些值时,可以将Debug Information Format改回DWARF with dSYM file,clean(必须)之后重新编译即可。

ps:4.2的解决方案为Xcode的默认设置,进行反向设置时,编译速度改变不大;

### 五、Link-Time Optimizations 链接时优化

Link-Time Optimization执行链接时优化(LTO)。在Clang/LLVM领域,这意味着链接器获得的是LLVM字节码,而不是通常的目标文件。这些字节码在一种更抽象的层次上代表程序这里写链接内容的执行过程,允许LTO得以进行,但是坏处是,仍然需要将他们转换成机器代码,在链接时需要额外的处理时间。

参数设为YES时,能够优化链接时间;目标文件以LLVM二进制文件格式存储,在链接期间,优化了整个程序。
将其设为NO时,可以减少Link阶段的时间。对于Link阶段耗时较长的项目,整体编译优化体现较为明显。

### 六、加装SSD固态硬盘

固态硬盘传输速度能达到500MB/s,其中读取速度达到400-600MB每秒,写入速度达到200MB每秒。而传统硬盘读取速度极限也无法超越200MB每秒,写入速度在100MB每秒左右。如果遇到非连续的散片数据,SSD能体现出极快的读写速度。而传统机械硬盘由于磁头寻道等原因,传输速度偏慢。

SSD加快了程序的I/O速率,读写速度比普通硬盘快,从而提升Xcode的编译速度。

###七、安装包大小优化 Asset Catalog Compiler - Options Optimization
Build Setting->Asset Catelog Compiler -> Options

在Optimization优化设置项有三个选项,不制定、time和Space

Optimization nothing是Xcode默认的设置。 与预想的不同,在选择Optimization time 时,编译时长并没有得到优化。 但在Optimization space时,编译耗时基本没有波动,但编译生成的app 大小有不小程度的优化。

### 八、安装包大小优化 Flatten Compiles XIB Files
是否流畅编译XIB文件

>指定是否在编译时剥离storyBoard文件以优化它们的大小,设为YES时编译出来的文件会被压缩,但是不能编辑。

### 九、安装包大小优化,清理未被使用的图片资源

**LSUnusedResources**
 
项目的开发过程总是会经历较长期的迭代,不断的添加功能的同时会引入大量的图片资源。需求变更、业务逻辑修改等需要移除某些功能模块时就会导致这些前期加入的图片资源问价被忽略而遗留在编译的安装包中,长此以往会使得安装包变得格外臃肿。特别是类似于手Q项目的开发,开发人员多,产品迭代频繁,开发时间紧俏,开发人员轮换等特点更有可能导致这样的后果。

一个较为传统的清理方法时将图片资源的文件名一一复制粘贴到Xcode的全局变量查找中去查找该字符串,如果返回的结果为零,则该资源很有可能没被使用。之所以是“很有可能”,是因为在代码中,资源有时是通过字符串拼接的方式进行引用的。

在这里提供一个github上的开源工具 LSUnusedResources ,这个工具是对github上的另一个开源工具Unused的优化改进(匹配速度、结果准确性),作者针对源码、Xib、Storyboard 和 plist 等文件,先全文搜索其中可能是引用了资源的字符串,然后用资源名和字符串做匹配,从而找出未被使用的资源,比Unused的查找速度要快得多。

使用起来也比较简单:
 1、将工程目录路径拷贝到Folder或通过Browse浏览文件目录;
 2、在Resource指定要查找的资源类型; (经过本人测试,发现该工具在未指定Resource类型时所查找出来的资源不是很准确,列举出 的资源事实上是正在使用的,所以我在测试时指定查找了png类型的文件。) 
3、单击Search以查阅结果。

注:为了避免对资源的误删操作,建议在该工具输出结果后对结果中的资源名复制并在Xcode的全局查找
中进行校验。

下载安装:[LSUnusedResources.app.zip](https://github.com/tinymind/LSUnusedResources/raw/master/Release/LSUnusedResources.app.zip)
 Github地址:[LSUnusedResources](https://github.com/tinymind/LSUnusedResources) 
参考链接:[查找XCode工程中没被使用的图片资源](http://blog.csdn.net/qq_25131687/article/details/blog.lessfun.com/blog/2015/09/02/find-unused-resources-in-xcode-project/)

### 十、安装包大小优化Deployment Postprocessing和Strip Linked Product

Xcode中Strip Linked Product 的默认设置为YES,但是Deployment Postprocessing的默认设置为NO。在Deployment Postprocessing 是Deployment的总开关,所以在打开这个选项之前 Strip Linked Product是不起作用的。

ps:当Strip Linked Product设为YES的时候,运行app,断点不会中断,在程序中打印`[NSThread callStackSymbols]`也无法看到类名和方法名。而在程序崩溃时,函数调用栈中也无法看到类名和方法名。

打开这两个选项之后进行编译,编译出的安装包有了较大程度的优化。

### 十一、安装包大小优化 Linking->Dead Code Stripping

将Dead Code Stripping 设置为YES 也能够一定程度上对程序安装包进行优化,只是优化的效果一般,对于一些比较小的项目甚至没有什么优化体现,所以这里也就没有上测试数据。

Dead Code Stripping 是对程序编译出的可执行二进制文件中没有被实际使用的代码进行Strip操作

推荐阅读更多精彩内容