连接器,我的理解是将高级语言编译成二进制文件在内存中运行的中间桥梁,最主要的作用是将符号绑定到地址上.
IOS用的是LLVM(Low Level Virtual Machine)编译器,编译器可以将代码编译成二进制并在内存中运行,这样可以极大提升程序运行的速度.同时缺点在于调试周期比较长.每次都需要重新启动才能看得到代码运行效果,所以才有了基于runtime机制产生的RN flutter 等跨端解决方案.
我一开始接触ios开发的时候用的是xcode5当时Apple还在使用GCC编译器,后来就换成LLVM,编译器会将oc源码编译成match-o文件,其中的lld内置连接器会将多个match-o文件给合并成一个.
LLVM编译过程:源码->AST-IR-Mactch-O(ios)
编译时连接器做了哪些工作:
Mach-o文件中主要还是代码和数据.代码数函数,数据是全局变量,连接器是将这些符号绑定在运行地址中.
我们写的代码编译器是将其翻译成一个个机器码,需要连接器将其串起来才能运行.就好比珍珠和线一样.
连接过程中要对代码进行检查,因为咱们在写代码的时候不同文件之间会有相互引用的代码,连接器会检查不同文件的相互引用,一些变量有没有定义,对项目中不同的文件变量进行地址重定位.随着项目不断迭代很多无用业务代码,链接去会检测函数调用关系,以main为源头,跟随每个引用,将引用的函数标记活的.自动将无用的代码去掉.
说完自己书写的代码,接下来看看在项目中会引用各种系统库和三方库,链接去是怎样处理他们的呢?
导入的库有静态库和动态库,静态库是在编程过程中连接到mach-o文件中,而动态库是在运行时通过dyld实现动态加载.但是在mach-o文件中包含了动态库的名字和地址,dlopen 和 dlsym 导入动态库时就可以实现动态运行它们;
dyld 会先执行mach-o文件 根据文件中的undefined标记的符号来加载对应的动态库,系统会开辟一个共享空间来解决递归依赖问题;加载后将undefined的符号绑定到动态库的地址上;最后再处理+load方法.main函数执行.
参考文献:
https://time.geekbang.org/column/article/86840
http://llvm.org/pubs/2004-01-30-CGO-LLVM.html
http://llvm.org/docs/LangRef.html?highlight=dead%20code%20stripping