编译器
1.编译/解释
- 编译型语言
代码须编译成机器码才能在CPU上执行的语言,如OC和Swift,其优点是代码执行效率高。
- 解释型语言
解释型语言,如 JavaScript 和 Python,代码不需要经过编译器,而是通过解释器直接将代码解释成CPU可以执行的代码。编写灵活,但执行效率低一些~
本篇主要关注编译型语言。编译过程可以划分为前端和后端两部分:
2.Clang
编译器前端将不同的高级编程语言经过词法分析、语法分析转化为与前端语言无关的统一的中间表示。iOS 中的编译器前端使用的是 Clang,它是一个 C++ 编写的、基于LLVM 的 C/C++/Objective-C/Objective-C++ 编译器。其主要任务是处理一些和具体机器无关的针对语言的分析操作:
预处理:
- 符号化 (Tokenization)
- 宏定义的展开
- #include 的展开。
语法和语义分析:
- 将符号化后的内容转化为一棵解析树 (parse tree)
- 解析树做语义分析(包含类型检查和其他检查)
- 输出一棵抽象语法树(Abstract Syntax Tree* (AST))
生成代码
- 将 AST 转换为更低级的中间码 (LLVM IR)
3.LLVM
编译器后端负责优化中间代码并生成对应平台的汇编代码。LLVM 属于编译器后端,其主要作用是:
代码优化
- 对生成的中间码做优化
- 生成特定目标代码
- 输出汇编代码
汇编器
- 将汇编代码转换为以.o 结尾的目标对象文件(将可读的汇编代码转换为机器代码)。
链接器
- 读取目标文件和库并解决未知符号问题,将它们编码进一个可执行文件或动态库中。
4.Bitcode
一般的编译流程:OC -> Clang AST -> CIL IR -> LLVM IR -> MIR -> Mach-O。
Bitcode 是 LLVM 中引入的一种中间代码,处在编译的LLVM IR这一阶段,尚未到 MIR 这一步。
Bitcode 提交到商店后,后续构建过程交给苹果完成。步骤之一就是剥离二进制符号(Binary symbol stripping),即从二进制文件中删除非必要的元数据,以便优化二进制文件的大小。
发布新款设备后,苹果可以用 Bitcode 生成对应芯片的MIR,无需开发者重新打包上传。
更新:苹果曾强推 Bitcode,但 Xcode 14 后遭到弃用~
5.Other link
在TARGET -> Build Settings -> Linking -> Other linker Flags 中可配置链接参数。
- -all_load
将静态库中所有文件都加载到IPA里,包括没用的一些文件、函数等。
- -noall_load
链接器默认的配置,只加载用到的那部分代码,没用到的会被优化掉,减小包体。
1 |
|
- -force_load<文件>
加载指定的某些文件。导入静态库后找不到文件或方法,推荐使用这个参数。
- -ObjC
如果工程里有OC和C++等代码,只将所有ObjC文件(.m、分类等)编译到IPA里。
题外话:
1、可以使用合适的链接参数,优化包体大小。
2、如果工程与静态库里定义了同名类或函数,但仅仅是将此静态库打包到了工程里,并没有 import 或使用它,此时是不会报错的。因为链接时默认使用 -noall_load 参数,不会链接没用到的类和函数,应用的二进制中只包含我们自己的那个类和函数。反之如果使用 -all_load 或 -ObjC 参数,即使没用到,也会报错。
6.Xcode Build
Xcode 是根据 target 分别编译的。每个 target 的具体的编译过程可以通过展开日志查看。
基本的格式是先简明一句说明要干什么,再缩进的几行说明具体的操作。
1 |
|
从上面日志可以看到,大致的过程是:
- compile swift文件
- compile各个.m文件(按名字升序)
- compile xib
- compile storyboard
- link storyboards
- copy静态资源,包括img,string,font
- compile asset catalogs
- run custom shell script
- process info.plist
- copy Swift standard libraries into xx.app(拷贝swift标准库)
- sign app(代码签名)
- touch app(生成.app文件)
- validate app(真机build有)
参考文章: