【iOS】iOS编译链接过程

13 篇文章 0 订阅
4 篇文章 0 订阅

iOS编译链接

编译流程:

预处理(Prepressing)–〉编译(Compilation)–〉汇编(Assembly)–〉链接(linking)
在这里插入图片描述

“ESc iso”

预处理

作为编译的第一步,首先将main.m文件编译成了main.i文件,指令如下:

clang -E main.m -o main.i

预编译是要处理源代码中以#开头的所有预编译指令。规则如下:

  • #define删除,并展开对应的宏定义
  • 处理所有的条件预编译指令。如#if、#ifdef、#else、#endif
  • 删除所有的注释//、/**/等
  • 添加行号和文件名标识。如#1 “main.m” (编译调试会用到)

编译

将main.i文件编译成main.s文件,指令如下:

clang -S main.i -o main.s

这个过程就是”上面的main.i文件进行:词法分析、语法分析、静态分析,优化生成相应的汇编代码,最终生成main.s文件。

  • 词法分析 :把源代码的字符序列分割成一个个token(关键字、表示符、字面量、特殊符号),比如把标识符放到符号表里面。
  • 语法分析 : 生成抽象语法树AST,此时运算符号的优先级确定了;有些符号具有多重含义也确定了,比如:*是乘号还取地址操作符;表达式不合法、括号不匹配等等,都会报错。
  • 静态分析 :分析类型声明和匹配问题。比如整型和字符串相加,肯定会报错。
  • 中间语法生成 : CodeGen根据AST(抽象语法树)自上向下逐步翻译成LLVM IR,并且对在编译期就可以确定的表达式进行优化,比如代码里面的a=1+3,可以优化成a=4。(假如开启了bitcode)
  • 目标代码生成与优化 : 根据中间语法生成依赖具体机器的汇编语言;并优化汇编语言。这个过程中,假如有变量且定义在同一个编译单元里,那么就给这个变量分配空间,确定变量的地址。假如变量或者函数不定义在这个编译单元里面,那就等到链接的时候才能确定地址。

汇编

将main.s文件编译成main.o文件(也就是我们常说的目标文件),指令如下:

clang -c main.s -o main.o

这个过程就是把上面得到的main.s文件里面的汇编指令翻译成机器指令,最终生成得到main.o

链接

这个过程就是将main.o编译成对应的Mach-O文件,也就是我们常说的可执行文件,指令如下:

clang main.o -o main

链接的本质就是把一个或多个目标文件和需要的库(静态库/动态库,如果需要的话)组合成一个文件(Mach-O可执行文件)

在这里插入图片描述

动态库和静态库

静态库

之所以称为静态库,是因为在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。

静态库特点:

  • 静态库对库函数的链接是放在编译时期完成的
  • 程序在运行时与函数库再无瓜葛,移植方便
  • 浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库北链接合成一个可执行文件

动态库

动态库在程序编译时并不会被链接到目标代码中,而是在程序运行时才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份共享库的实例,规避了空间浪费的问题。动态库在程序运行时才被载入,也解决了静态库对程序的更新,部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新

动态库特点:

  • 动态加载和链接: 动态库在程序运行时才被加载,而不是在编译时链接进程序。这意味着程序只有在需要使用动态库的功能时,才会把相应的代码加载到内存中。
  • 代码共享: 多个程序可以共享使用同一份动态库,从而减少了程序的大小和内存占用。当动态库被更新时,所有使用它的程序都能自动获得更新。
  • 灵活性: 使用动态库可以让程序更加灵活,因为程序不需要在编译时就确定依赖哪些库。程序员可以在运行时决定加载哪些动态库,并可以在不需要时卸载。
  • 独立性: 动态库的更新不会影响到使用它的程序本身。程序只需要链接到正确版本的动态库即可,无需重新编译程序。
  • 跨平台性: 动态库的接口定义是跨平台的,因此使用动态库的程序能够在不同操作系统上运行,无需修改源代码。

静态库和动态库的区别

编译方式的不同:

  • 静态库是在编译时将库的代码打包到可执行程序中,因此生成的可执行程序包含了所有用到的库函数的代码。这样,点那个程序被调用时,需要使用哪些库函数就直接从可执行文件中取出来使用。因为代码打包进了可执行程序中,因此静态库的生成通常需要在代码的编译阶段进行。
  • 动态库则是在运行时动态加载到程序中的,因此生成的可执行文件并不包含库函数的实现代码,而只是引用了动态库的接口。当程序调用到该库函数时,操作系统会将该函数从动态库文件中加载到内存中供程序运行使用。这样一来,程序的可执行文件会比静态库生成的可执行文件小很多因为代码加载是在程序运行时进行的,所以动态库的链接通常是在程序运行之前进行。

内存使用方式不同:

  • 由于静态库的代码被打包进了可执行程序中,所以在程序运行时,静态库中的代码被复制到了程序使用的内存中,并一直驻留在内存中使用,因此不需要占用额外的内存空间。
  • 而动态库的代码在程序运行时才会被夹在到内存中,因此动态库的代码实现被复制进内存,会占用额外的内存空间。但是与静态库相比,动态库的内存使用方式具有更好的空间和性能优势,因为多个程序可以共享同一个动态库,而不需要重复加载相同的库文件,从而减少了系统的内存占用。

更新和维护方式不同:

  • 静态库的代码被打包成可执行程序的一部分,因此静态库的更新和维护需要重新进行编译和部署,才能让所有使用了该静态库的程序都能得到更新的代码。
  • 动态库可以独立于程序进行更新,因为动态库作为一个单独的文件存在于系统中,可以被多个程序共享。因此,当需要更新动态库时,只需要替换掉旧的动态库文件,不需要重新编译和部署所有使用了该动态库的程序

因此,如果需要多个程序共享同一个库,或者需要较少的内存占用,则使用动态库更为合适。如果需要保持部署和更新的稳定性,则静态库可能更为合适。

dyld

link
dyld(the dynamic link editor)是苹果的动态链接器,是苹果操作系统一个重要组成部分,在系统内核XNU完成Mach-O文件的加载,做好程序准备工作之后,交由dyld负责余下的工作。在maxOS系统中,dyld位于D/usr/lib/dyld

dyld2.0和dyld3.0的区别

dyld3 包含这三个部分:

  • 进程外Mach-o分析器和编译器(out-of-process mach-o parser)

由于dyld2存在的问题,dyld3中将采用提前写入把结果数据缓存成文件的方式构成一个 lauch closure(可以理解为缓存文件)

  • 进程内引擎 执行 launch closure 处理 (in-process engine)

验证“launch closure”是否正确,映射dylib,执行main函数。此时,它不再需要分析mach-o header和执行符号查找,节省了不少时间。

  • launch closure缓存服务(launch closure cache)

系统程序的launch closure直接内置在shared cache中,而对于第三方APP,将在APP安装或更新时生成,这样就能保证launch closure总是在APP打开之前准备好。

  • 28
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值