文章目录
- 前言
- 计算机语言
- 文件后缀名
- 编译和链接的过程
- 编译链接
- 预处理(预编译)-> 产生.i文件
- 编译 -> 产生.s文件
- 汇编 -> 产生.o文件
- 链接
- 总结
前言
就我而言,iOS开发的过程中接触到的编译链接方面的知识很少,这部分知识还是很重要的。
对于iOS的编译链接过程来说并不难,和微机原理的汇编过程还是挺像的。今天对于编译链接的过程学习和了解一下。
参考:iOS程序员的自我修养-编译、链接过程
参考:iOS编译过程
计算机语言
计算机语言有机器语言,汇编语言和高级语言。对于OC这种高级语言来说分为 编译语言和解释型语言。
- 编译型语言(一次性翻译)
-
- 特点:一次性翻译。
-
- 编译型语言的程序只要等编译器编译之后,每次运行都可以直接运行,OC和swift就是如此。
-
- 优点就是执行速度够快,因为不用多次编译。
-
- 缺点就是可移植性差,编译的时候需要对操作系统的库做出链接,可能需要不同的库。
- 解释型语言(逐步翻译)
-
- 解释语言编写的程序在每次运行时都需要通过解释器对程序进行动态解释和执行,如
php,javascript
等
即解释一条代码,执行一条
- 解释语言编写的程序在每次运行时都需要通过解释器对程序进行动态解释和执行,如
-
- 优点就是可移植性好,不需要繁杂的系统库。
-
- 缺点显而易见就是执行速度慢,不是一次性的。
文件后缀名
在iOS的编译和链接过程中,不同的文件后缀名代表不同的文件类型和中间过程。
-
.i
文件:.i
文件是预处理器处理源代码之后生成的文件,其中包含了宏展开、条件编译等预处理操作后的代码。 -
.s
文件:.s
文件是汇编器生成的汇编代码文件。它将预处理器生成的.i
文件翻译成机器指令的文本表示形式,每个汇编指令对应一条机器指令。 -
.o
文件:.o
文件是编译器生成的目标文件,也被称为对象文件。它包含了汇编器生成的机器指令以及一些符号表、重定位信息和其他调试信息。 -
.m
文件:.m
文件是 Objective-C 源代码文件的常见扩展名。它包含了 Objective-C 的代码,可以与 C 和 C++ 代码混合使用。
这些文件在编译和链接过程中的不同阶段产生,并在整个过程中相互转换和传递,最终生成可执行文件或库文件。
编译和链接的过程
上面说过OC/swift都是编译语言,在执行的时候通过编译器生成机器码,机器码可以直接在CPU上执行,效率更快。
OC的编译是基于Clang/ LLVM来编译的,简单了解LLVM是一个模块化的可重用的编译器和工具技术的集合,Clang是LLVM的子程序,C,C++,OC都是Clang的子程序。其目的就是更快的效率编译出更好的程序。
编译链接过程:预处理 -> 词法分析 -> 语法分析 -> 静态分析 -> 生成中间代码和优化 -> 汇编 -> 链接 =
预处理 -> 编译 -> 汇编 -> 链接
编译链接
1,预处理:macro 宏, import 头文件替换及处理其他的预编译指令,产生.i文件
。(都是以#号开头)
2,编译:把预处理完的一系列文件进行一系列词法、语法、语义分析,并且优化后生成相应的汇编代码,产生.s文件
;
3,汇编:汇编器将汇编代码生成机器指令,输出目标文件,产生.o文件
(根据汇编指令和机器指令的对照表一一翻译就可以了);
4,链接:在一个文件中可能会到其他文件,因此,还需要将编译生成的目标文件和系统提供的文件组合到一起,这个过程就是链接。经过链接,最后生成可执行文件
。
经过编译和链接,才会把写的代码转换成计算机能识别的二进制指令。
预处理(预编译)-> 产生.i文件
clang -E main.m -o main.i
处理源代码文件中的以"#"开头的预编译指令
- "#define"删除并展开对应宏定义。
- 处理所有的条件预编译指令。如
#if/#ifdef/#else/#endif。
"#include/#import"
包含的文件递归插入到此处。- 删除所有的注释"//或/**/"。
- 添加行号和文件名标识。
如“# 1 "main.m"”
,编译调试会用到。
总结编译器在预处理阶段处理结果:
- 宏替换 (在源码中使用的宏定义会被替换为对应的内容)
- 头文件引入
(#include,#import)
使用对应文件.h的内容替换这一行的内容,所以尽量减少头文件中的#import,使用@class替代,把#import放到.m文件中。) - 处理条件编译指令
(#if,#else,#endif)
编译 -> 产生.s文件
clang -S main.i -o main.s
编译过程也分为 词法分析 -> 语法分析 -> 静态分析 最后优化生成相应的汇编代码,得到.s文件
- 词法分析 :这一步把源文件中的代码转化为特殊的标记流,源码被分割成一个一个的字符和单词,在行尾Loc中都标记出了源码所在的对应源文件和具体行数,方便在报错时定位问题。
- 语法分析:这一步是把词法分析生成的标记流,解析成一个抽象语法树(abstract syntax tree – AST),同样地,在这里面每一节点也都标记了其在源码中的位置。此时运算符号的优先级确定了;有些符号具有多重含义也确定了,比如“*”是乘号还是对指针取内容;表达式不合法、括号不匹配等,都会报错。
- 静态分析 :分析类型声明和匹配问题,如出现方法被调用但是未定义、定义但是未使用的变量等,以此提高代码质量。
-
- 类型分析:在此阶段clang会做检查,最常见的是检查程序是否发送正确的消息给正确的对象,是否在正确的值上调用了正常函数。如果你给一个单纯的
NSObject*
对象发送了一个hello
消息,那么 clang 就会报错,同样,给属性设置一个与其自身类型不相符的对象,编译器会给出一个可能使用不正确的警告。
- 类型分析:在此阶段clang会做检查,最常见的是检查程序是否发送正确的消息给正确的对象,是否在正确的值上调用了正常函数。如果你给一个单纯的
-
- 其他分析:关于是否多次初始化的检查。
- 中间代码生成和优化: LLVM 会对代码进行编译优化,例如针对全局变量优化、循环优化、尾递归优化等,最后输出汇编代码。
- 目标代码生成和优化: 根据中间语言生成依赖具体机器的汇编语言。并优化汇编语言。
汇编 -> 产生.o文件
clang -c main.s -o main.o
在这一阶段,汇编器将上一步生成的可读的汇编代码转化为机器代码。最终产物就是 以 .o 结尾的目标文件。使用Xcode构建的程序会在DerivedData目录中找到这个文件。
链接
clang main.o -o main
这一阶段是将上个阶段生成的目标文件和引用的静态库链接起来,最终生成可执行文件,链接器解决了目标文件和库之间的链接。
使用clang main.m生成可执行文件a.out(不指定名字默认为a.out),使用file a.out
可以看到其类型信息:
a.out: Mach-O 64-bit executable x86_64
可以看出可执行文件类型为 Mach-O
类型,在 MAC OS 和 iOS 平台的可执行文件都是这种类型。
Mach-O(Apple官方文档)
总结
编译链接是一iOS文件从开始到变成可执行的文件的过程,我们不去研究原理,但是对于整个流程的掌握还是很有必要的。