说句实话,作为一个开发,平时每天在写代码,但是对于代码的编译
过程并不是十分的了解,最近看了戴老师的链接器这篇文章,大概有了一些了解,做个笔记。
项目开发完成,我们需要进行编译来看修改后的效果,但是假如编译的是一些大型的项目,那么恐怕编译时间将非常的长
,因此就需要对此进行一些优化,要优化必须先了解一下启动时链接器
所做的事情。简单来说,链接器
做的最重要的事情就是把符号绑定在地址上
。
在说链接器
之前,先来看看iOS
所使用的编译器
,在iOS
开发中,我们所写的代码会被编译器
转化为机器码
,然后CPU
会直接执行机器码
,除去编译器
,还有一种叫做解释器
,但是苹果并没有使用,因为苹果希望程序执行的效率更高,速度更快
。
为什么说解释器
的速度慢,效率不高呢?因为解释器
是在运行时
将我们写的代码,一句句的翻译成目标代码,然后再一句句的执行目标代码,这个效率肯定就是不如事先生成一份完整的机器码再去执行。
那么为什么解释器
效率低还有人会用呢?学iOS
的人一定会对运行时
比较敏感,解释器
是在运行时期间
发生作用的,那么使用解释器
就可以随时增加或者更新
代码来改变项目内容,无需重启
项目就可以看到代码更新的结果,而且如果项目上线出现问题,也可以随时更新,用户无需更新即可升级使用。这就大大缩短了程序开发周期和功能更新周期。
好了现在弄清楚了苹果使用编译器
的原因,我们就来看看苹果使用的是什么编译器
,相信大家和我一样,总看到LLVM
,也知道它是苹果的编译器
,但是对它了解的并不是很多,在Xcode5
之前,苹果的编译器使用的是GCC
,后来换成了LLVM
,它的速度要比GCC
高出三倍
之多。
LLVM
是编译器工具链技术的一个集合,而其中的lld
就是内置链接器
。编译器会对每个文件进行编译,生成Mach-O
(可执行文件),链接器
将会把生成的Mach-O
合并成为一个。
LLVM
的工作简单来说可概括为几点:
1.咱们写好的代码,LLVM
会预处理一下,比如把我们定义的宏嵌入到相应的位置。
2.预处理完成之后,LLVM
会对代码进行词法分析
和语法分析
,生成AST
,AST
就是抽象语法树
,结构上比代码更精简,可以更快速的遍历,可以更快的生成IR
(中间表示)。
3.最后AST
会生成IR
,IR
是一种更接近机器码
的语言,通过IR
可生成多份适合不同平台的机器码,对于iOS系统
来说,生成的就是Mach-O
。
那么链接器做了什么呢?通俗点说,Mach-O里面的主要内容就是我们的代码和数据
,代码就是我们所写的函数
,也就是各种方法,数据就是我们定义的全局变量
,无论是方法,还是各种全局变量,它们的实例
都需要和方法名称,变量名称
联系起来。当我们的系统想要操作各种方法
,各种变量
时,必然需要找到方法名称
,变量名称
所对应的地址
,而链接器所做的工作就是将方法,变量名称
所对应的地址关联
起来。
那么链接器
为什么还要把生成的多个Mach-O合并成为一个
呢?我们平时写代码肯定是不会把所有的内容都写在一个文件里面,一个项目一般会包含很多文件,而各个文件之间的方法接口,数据都是相互依赖
的,因此单个文件
生成的Mach-O
是肯定无法正常运行的,如果这个文件调用了其他文件中的方法,那么就无法找到这个方法的地址
,也就无法执行。
链接器
在链接多个目标文件的时候会创建一个符号表
,记录所有已经定义,和未定义的符号,我们在编译项目的时候经常会遇到这个错误“ld: dumplicate symbols”
,这就说明项目里面有重复的文件,也会遇到这个错误“Undefined symbols”
这就说明项目中缺少文件。
链接器还有一个作用,就是会自动去除
长时间没有使用的函数,使其不会被打包进入Mach-O
文件,如果我们项目迭代的过程中,废弃
了一些函数,不再调用,久而久之,被废弃的函数越来越多,我们的Mach-O
会变得越来越大。链接器
在整理函数符号调用关系
时就会自动去除掉废弃
的函数。链接器在整理函数符号调用关系时,会以main函数
为源头,跟随每个引用,被跟踪到的会标记为live
,未被标记的就是无用函数,链接器就会自动去除无用函数。
以上就是这一章的部分笔记,最后还是推荐大家去看原文,大家共同进步!