「这是我参与2022初次更文挑战的第11天,活动概况查看:2022初次更文挑战」
iOS开发言语,不论是OC
仍是Swift
,都是经过LLVM
进行编译的,终究生成.o
文件,其编译流程如下图:
-
OC
经过clang
编译器,编译成IR
,然后再生成可执行文件.o
(也便是咱们的机器码); -
Swift
则是经过Swift
编译器生成IR
,然后再生成可执行文件.o
;
Swift编译流程
Swift
言语编译流程图如下:
-
Swift
代码经过-dump-parse
命令进行语法剖析
,生成笼统语法树AST
; -
笼统语法树
经过-dump-ast
进行语义剖析
(比方类型查看是否正确,是否安全); -
语义剖析
之后,Swift
代码将会降级为SIL
,也便是Swift中心言语
(Swift intermediate language); -
SIL
分为Raw SIL
(原生的,没有开启优化选项)和SILOpt Canonical SIL
(经过优化的); - 终究经过
LLVM
降级为IR
,然后经过后段代码编译为不同架构的机器码
;
流程中触及的命令如下:
// 剖析输出AST
swiftc main.swift -dump-parse
// 剖析并且查看类型输出AST
swiftc main.swift -dump-ast
// 生成中心体言语(SIL),未优化
swiftc main.swift -emit-silgen
// 生成中心体言语(SIL),优化后的
swiftc main.swift -emit-sil
// 生成LLVM中心体言语 (.ll文件)
swiftc main.swift -emit-ir
// 生成LLVM中心体言语 (.bc文件)
swiftc main.swift -emit-bc
// 生成汇编
swiftc main.swift -emit-assembly
// 编译生成可执行.out文件
swiftc -o main.o main.swift
相关于OC
,在Swift
的编译进程中多了SIL
,SIL
会对咱们的代码进行安全查看,比方如下代码:
在OC
中:
咱们定义的int8_t
类型的计算成果将会出错,由于int8_t
只有 一个字节,其运算成果现已溢出,在OC
中给出了一个过错的运算成果;咱们再来看在Siwft
中会产生什么:
咱们看到,同样的代码,在Swift
中的编译阶段就会报错,防止后续产生不可预知的过错;这些都是由于SIL
存在的成果;
SIL文件剖析
那么SIL
究竟有着什么样的语法规矩呢?咱们来生成一个SIL
文件进行剖析;咱们先写一段简略的代码:
接下来咱们将这段代码生成SIL
文件:
为了方便观看,咱们也能够经过swiftc main.swift -emit-sil > ./main.sil
指令将SIL
文件输出为main.sil
文件;
由于SIL
文件内容较多,咱们只挑选重要的部分剖析:
Teacher的声明
首要,咱们先看一下Teacher
在SIL
阶段的声明部分:
从Teacher
的定义中能够看到有初始化的存储属性age
和name
,还有一个标识为@objc
的deinit
函数,以及默许的初始化器init()
;
main函数
-
@main
:标识入口函数,在SIL
中@
是作为标识符
的; -
%0
—%9
:这写在SIL
中也被称为寄存器
,能够理解为开发进程中的常量,一旦赋值,将无法再次修改(所以后边数字会一向累加);需求注意的是,此处的寄存器
是虚拟寄存器
,终究运转到详细的设备上时,会运用真实的寄存器
; -
alloc_global @$s4main1tAA7TeacherCvp
:分配一个全局变量,该全局变量的名称是混写
的,咱们能够经过终端指令xcrun swift-demangle
将其还原:
能够看到此处对应的是main
文件中的t
变量,对应的是main
中的Teacher
;
-
%3 = global_addr @$s4main1tAA7TeacherCvp
:拿到该全局变量
的地址给%3
; -
%4 = metatype $@thick Teacher.Type
:获取Teacher.Type
的元类型给%4
; - 依据注释能够知道
%5
是Teacher.__allocating_init()
函数的引证,也便是其指针地址; -
%6 = apply %5(%4)
:将%5(%4)
的成果,也便是Teacher
的实例变量,赋值给%6
; -
store %6 to %3
:将%6
也便是实例变量的内存地址,存放到%3
这个全局变量中; - 在
Swift
底层中Int
便是一个Struct
类型,%8
和%9
是在构建一个Int32
的整数类型0
,类似与咱们OC
中main
函数终究的return 0
;
关于SIL
语法规矩,请查看SIL官方文档
__allocating_init()函数
在上述SIL
代码中调用了s4main7TeacherCACycfC
也便是Teacher.__allocating_init()
函数来创立当前的实例对象的;咱们在SIL
中定位到该函数:
- 该函数需求一个
Teacher.Type
的元类型,咱们能够将此元类型理解为isa
; -
%1 = alloc_ref $Teacher
:alloc_ref
会创立一个Teacher
的实例变量,其引证计数初始化为1
;alloc_ref
实际上也便是去堆区
请求内存空间;假如标识为objc
的Swift类
将会运用Objective-C
的+allocWithZone:
初始化办法;怎么验证呢?咱们在代码中增加如下断点:
运转程序,查看汇编指令:
咱们看到将会调用Teacher.__allocating_init()
函数,那么该函数是怎么完成的呢?断点进入该函数:
在__allocating_init()
函数中主要调用了swift_allocObject
和Teacher.init()
;
-
swift_allocObject
在堆区找到合适的内存空间初始化; -
Teacher.init()
初始化成员变量;
这是一个朴实的Swift
类,那么假如咱们将Teacher
继承自NSObject
会产生什么呢?
咱们重复上述汇编调试步骤,进入Teacher.__allocating_init()
函数:
咱们发现,初始化函数变成了objc_allocWithZone
以及objc_msgSend
;
-
objc_allocWithZone
调用malloc
函数请求内存空间; -
objc_msgSend
发送init
音讯;
swift_allocObject
在前边咱们现已剖分出Swift
类初始化进程中会调用swift_allocObject
,那么该函数做了什么呢?咱们需求借助于Swift源码进行剖析;
在该目录下找到stdlib->public->runtime->HeapObject.cpp
文件,该文件是和咱们的Swift
类初始化相关的文件;在该文件中定位到_swift_allocObject_
函数,这是一个私有函数,其是被swift::swift_allocObject
调用起来的:
_swift_allocObject_
完成如下:
该函数有三个参数:
-
HdapMetadata const *metadata
:元数据类型; -
requiredSize
:所需求的巨细; -
requiredAlignmentMask
:对齐所需求的掩码,能够从objc
的源码中得知,其为7
,由于是8
字节对齐;
将requiredSize
和requiredAlignmentMask
传递给函数swift_slowAlloc
,该函数返回了一个HeapObject
类型的指针; reinterpret_cast
用来做指针类型
的转化;
-
new (object) HeapObject(metadata)
:HeapObject
初始化;
那么swift_slowAlloc
是用来干什么的呢?
swift_slowAlloc
其完成如下:
在swift_slowAlloc
函数中,调用了malloc
函数来开辟内存空间;
流程总结
那么,咱们大致能够总结出Swift
对象进行内存分配的流程:
- 首要会调用
_allocating_init()
:该函数有编译器
生成; - 关于纯
Swift
类将会再调用swift_allocObject()
函数; - 然后在
swift_allocObjec()
总会调用私有函数_swift_allocObject
; - 然后经过函数
swift_slowAlloc
调用malloc
来请求堆区
的内存空间;