简单介绍 - LLVM编译器
苹果使用的是LLVM编译器,LLVM架构设计的非常好,主要分为前端,中间,后端
-
Frontend 前端 主要是将编程代码进行预处理、词法分析、语法分析、语义分析,然后生成对应的中间代码(这个时候的产物已经与机器没有关系了,可以理解为任何机器都通用的)
-
Optimizer 中间 主要是将前端的输出进行优化
-
Backend 后端 主要是针对不同的机器生成对应的指令代码
编译过程:
1.预处理
主要处理文件中以#开头的预编译命令 比如宏定义替换,引入的头文件进行内容替换
2.词法分析
将代码分解成一个一个独立的词法符合(Token)
3.语法分析
将词法分析的结果生成对应的抽象语法树,并验证语法的正确性
4.语义分析
对整个语句进行判别是否有问题
5.生成中间代码
编译器前端的产物,生成与机器无关的中间代码。
-
Class/Meta Class/Protocol/Category 内存结构生成,并放在指定的section中。
-
Method/Ivar/Property内存结构生成
-
组成method_list/iva_list/property_list并填入Class
-
为每个ivar合成偏移量
-
将语法树中的ObjCMessageExpr翻译成相应版本的objc_msgSend等等
-
根据修饰符strong、weak、copy、atomic合成@property自动实现的setter/getter
-
生成block_layout的数据结构
-
block数据结构捕获相应的变量
-
分析对象引用关系,将objc_storeStrong/objc_storeWeak等ARC代码插入
-
自动实现调用【super dealloc】
-
为每个拥有ivar的class合成.cxx_destructor方法来自动释放类的成员变量
-
Bitcode 生成字节码
6.目标代码生成与优化
Optimizer对前端产物进行优化,再交给后端根据不同的机器指令集生成对应的机器代码,生成对应的Mach-O文件
7.对Mach-O,静态文件等进行link,生成可执行文件,写入对应的.app包里
8.拷贝项目中的资源文件到对应的.app包里
9.编译xib和storyboard
10.link xib和storyboard的产物
11.如果有自定义的脚本,执行脚本(Build Phase 里的Run Script 可以设置自定义的脚本)
12.处理info.plist文件(具体处理什么,不太清楚)
13.生成DSYM文件
14.如果有混编swift,就会将swift的标准库拷贝到对应的.app包里(所以混编oc和swift会增大包体积)
15.代码签名
出于安全考虑,为了防止代码被篡改,最后的可执行文件会被codeSign进行签名。app运行的时候会去校验签名
Mach-O简单介绍
Mach-O 是一种用户记录可执行文件、对象代码、共享库、动态加载代码和内存转储的文件格式。
Mach-O文件类型分为:
-
应用的主要二进制
-
dylib动态链接库
-
静态链接库
-
Bundle 不能被链接的Dylib,只能在运行时使用dlopen( )加载,可当做macOS的插件
-
可重定向文件类型
主要包含三个主要区域:
-
Header: 文件类型, 目标架构
-
Load command: 描述文件在虚拟内存中的逻辑与布局
-
Raw segment date: 原始数据
-
_TEXT 代码段 (方法名,类名,方法签名……)
-
_DATA 读取和写入数据的段 (全局变量,程序中的类的列表,自己实现+load方法的类,被引用的类列表,协议列表等等)
-
_LINKEDIT 动态连接器需要使用的信息
-
[图片上传失败...(image-9cc8ea-1581587495399)]
点击APP图标启动过程
内核先加载主程序
Load dylibs
dyld(动态链接器)启动,然后dyld开始递归加载所有的动态库(通过ImageLoader去加载)。
动态库有哪些好处?
-
代码共用 很多程序都动态链接了这些lib,但是内存和磁盘上仅有一份
-
易于维护 可以随时进行更新替换
-
减少了程序的可执行文件体积(运行时候才被加载)
Rebase
因为动态库是被加载到随机地址上的,所以要对所有库内部进行地址指针修正
Bind
修改库对外的指针
Objc Runtime
bind操作结束后,会读取二进制文件的DATA段,找到与objc相关的信息,注册objc类,确保selector的唯一性,读取protocol以及category的信息
Initializers初始化
调用objc类的+load函数
调用c++中带有constructor标记的函数
网友评论