背景介绍
自从混编Swift之后,Xcode编译的变慢了。主要体现在:
- 切换分支,要编译好一会,动不动就卡死。
- 改动一点Swift代码,也要编译好一会,苦不堪言。
团队的小伙伴也经常反馈编译慢的问题,一定程度上影响了开发效率。所以下定决心:好好优化一波。
作者的项目原本是OC项目,两年前进行Swift化,目前代码量再120万+行。
Swift化进度大概在70%。
OC代码逐步废弃过程中,本次编译优化,不包含OC相关的优化。
切换分支为什么变慢?
在Xcode中,当你切换分支,可能会导致编译时间大幅增加,原因主要包括以下几点:
- 缓存失效:Xcode使用一种高级缓存机制来优化编译时间。当你切换分支时,源代码会发生变化,这可能导致之前的编译缓存失效。因此,Xcode需要重新编译更多的文件,而不是仅仅那些自上次编译以来被修改过的文件。
- 项目依赖变化:不同分支可能有不同的项目配置和依赖关系。当你从一个分支切换到另一个分支时,这些配置和依赖的变化可能需要重新解析和编译。
- 索引重建:Xcode使用索引来支持诸如代码自动完成、快速跳转到定义等功能。分支切换可能导致Xcode需要更新或重建其索引,这个过程可能会占用额外的时间。
- 资源文件变化:如果不同分支中包含的资源文件(如图片、音频文件等)发生变化,Xcode可能需要更多时间来处理这些资源文件的变化。
- 清理和重建:在某些情况下,开发者在切换分支后可能会手动执行清理(Clean)操作,以确保从干净的状态开始编译。这意味着整个项目需要从头开始编译,自然会增加编译时间。
为什么改动一点Swift代码会导致变慢?
在使用Objective-C代码中引用Swift代码时,通常情况下是通过导入 xxx-Swift.h 头文件来实现的,该头文件包含了整个Swift模块的公开接口和符号的声明。
如果你只希望引入部分Swift代码而不是全部代码,目前的方式是无法实现的。由于 xxx-swift.h 是由xcode自动生成的,它包含了整个Swift模块的公开接口,因此无法选择性地导入。这意味着,如果你想在Objective-C中使用Swit代码,目前的解决方案是将整个Swift模块导入到Objective-C中,无法只选择部分导入。
这也就是改动一点Swift代码,编译也会慢点原因。
编译过程
可以更详细地探讨 XCode 编译过程的每个阶段:
-
预处理 (Preprocessing)
- 宏替换 :预处理器会处理以
#define
定义的宏,替换代码中的宏引用。 - 文件包含 :处理
#include
或#import
指令,将头文件的内容插入到源文件中。 - 条件编译 :根据
#if
,#ifdef
,#ifndef
,#else
,#elif
, 和#endif
指令,决定哪些部分的代码应该被编译。 - 移除注释:预处理器会移除代码中的注释。
- 宏替换 :预处理器会处理以
-
编译 (Compilation)
- 语法检查:检查代码的语法是否符合编程语言规范。
- 类型检查:验证变量和函数的数据类型,确保类型的正确性和一致性。
- 生成中间代码:编译器生成中间表示(Intermediate Representation,IR)的代码,这个代码是平台无关的。
- 优化:在生成目标代码之前,编译器会进行各种优化,比如循环优化,常量折叠等,以提高程序的运行效率。
-
汇编 (Assembly)
- 生成汇编代码:编译器将中间代码转换成汇编语言。
- 汇编成二进制代码:汇编器把汇编语言转换成机器码,即二进制代码,这是计算机可以直接执行的代码。
-
链接 (Linking)
- 解析符号:链接器会解析程序中使用的函数和变量的引用,确保它们都指向正确的地址。
- 合并对象文件:将所有的对象文件(编译生成的文件)和需要的库文件合并在一起,形成一个单一的可执行文件。
- 地址分配:链接器为函数和变量分配内存地址,并解决程序内部和外部模块之间的引用。
在 Xcode 中,这些步骤通常是隐藏的,但可以通过构建日志来查看。Xcode 还提供了丰富的配置选项,允许开发者自定义编译过程,如设置不同的编译标志、选择不同的编译器和链接器选项等。此外,Xcode 集成了 LLVM 编译器,提供了先进的优化技术和调试支持。