编译流程
OC
是通过Clang
编译器编译成IR
,然后再生成可执行文件.o
Swift
是通过swiftc
编译器编译生成IR
,然后再生成可执行文件.o
Swift编译流程图
主要分为以下几个过程:
-
Parsing(解析)
:解析器是一个简单的递归下降解析器(在 lib/Parse 中实现),带有一个集成的、手工编码的词法分析器。解析器负责生成没有任何语义或类型信息的抽象语法树(AST)
,并针对输入源的语法问题发出警告或错误
-
Semantic analysis(语义分析)
:语义分析(在 lib/Sema 中实现)负责获取解析后的AST
并将其转换为格式良好、经过完全类型检查的AST
形式,针对源代码中的语义问题发出警告或错误。语义分析包括类型推断,如果成功,则表明从生成的、类型检查的AST
生成代码是安全的
-
Clang importer(Clang导入器)
:Clang
导入器(在 lib/ClangImporter 中实现)导入Clang
并将它们导出的C
或Objective-C API
映射到相应的Swift API
。生成的导入AST
可以通过语义分析进行引用
-
SIL generation(SIL生成)
:Swift
中间语言(SIL)
是一种高级的、特定于Swift
的中间语言,适用于进一步分析和优化Swift
代码。SIL
生成阶段(在 lib/SILGen 中实现)将经过类型检查的AST
降低为所谓的“原始”SIL
。SIL
的设计在docs/SIL.rst 中进行了描述
-
SIL guaranteed transformations(SIL保证转换)
:SIL
保证转换(在 lib/SILOptimizer/Mandatory 中实现)执行影响程序正确性的附加数据流诊断(例如使用未初始化的变量
)。这些转换的最终结果是“规范的”SIL
。
-
SIL Optimizations(SIL优化)
:SIL
优化(在 lib/Analysis、lib/ARC、lib/LoopTransforms和 lib/Transforms 中实现)对程序执行额外的高级、特定于Swift
的优化,包括(例如)自动引用计数优化,去虚拟化和泛型专业化
-
LLVM IR Generation(LLVM IR 生成)
:IR
生成(在 lib/IRGen 中实现)将SIL
降低到LLVM IR,此时LLVM
可以继续优化它并生成机器代码。
生成SIL
- 指令:
swiftc -emit-sil main.swift >> ./main.sil
将swift
文件生成SIL
文件
对象
- 在
SIL
文件中, 调用了__allocating_init
函数去创建对象,在底层实质上是调用了swift_allocObject
函数,此时需要去 Swift源码(需要进行源码编译)
查看:
static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,
size_t requiredSize,
size_t requiredAlignmentMask) {
assert(isAlignmentMask(requiredAlignmentMask));
auto object = reinterpret_cast<HeapObject *>(
// 1.计算内存大小
swift_slowAlloc(requiredSize, requiredAlignmentMask));
// NOTE: this relies on the C++17 guaranteed semantics of no null-pointer
// check on the placement new allocator which we have observed on Windows,
// Linux, and macOS.
// 2.根据元数据类型创建实例对象
new (object) HeapObject(metadata);
// If leak tracking is enabled, start tracking this object.
SWIFT_LEAKS_START_TRACKING_OBJECT(object);
SWIFT_RT_TRACK_INVOCATION(object, swift_allocObject);
return object;
}
HeapObject 构造方法
constexpr HeapObject(HeapMetadata const *newMetadata)
: metadata(newMetadata) , refCounts(InlineRefCounts::Initialized) { }
// `metadata`是`HeapMetadata`类型的指针,占`8字节`
// 通过查找`refCounts`得到它是`InlineRefCounts`类型,占用`8字节`
总结:
-
- 对象内存分配流程:
__allocating_init
->swift_allocObject_
->swift_slowAlloc
->malloc
- 对象内存分配流程:
-
Swift
中实例对象
占用16字节
,比OC
中多了refCounted(引用计数大小)
类
当metadata
的kind
为Class
时
属性
存储属性
常量存储属性(let
修饰) 和 变量存储属性(var
修饰)
class Person {
let age: Int = 18 // 常量存储属性
var name: String = "zll" // 变量存储属性
}
class Person {
@_hasStorage @_hasInitialValue final let age: Int { get }
@_hasStorage @_hasInitialValue var name: String { get set }
@objc deinit
init()
}
- 在
Sil
文件中常量存储属性只能使用getter
方法,变量存储属性可以setter
和getter
方法
计算属性
- 计算属性不占用内存,它本身不存储值,通过
getter
间接访问值 - 计算属性没有存储属性拥有的
_hasStorage @_hasInitialValue
标识符
延迟属性
- 延迟存储属性的初始值在其第⼀次使⽤时才能访问(在访问
getter
时,才会对age
赋值),使用关键字lazy
来标识一个延迟属性,延迟属性必须要设置一个初始值
- 结果发现使用延迟属性后会导致内存增大
类型属性
- 类型属性属于这个类的本身,不管有多少个实例,类型属性
只有⼀份
,我们可以使⽤static
来声明⼀个类型属性 - 这个属性本质是调用
swift_once
函数,而其中是调用了gcd的dispatch_once_f
单例
属性观察者(willSet/didSet)
-
- 属性观察可以添加在
类的存储属性
、继承的存储属性
、继承的计算属性
中
- 属性观察可以添加在
-
- 父类在调用
init
中改变属性值不会触发
属性观察,子类调用父类的init
会触发
属性观察
- 父类在调用
-
- 统一属性在父类和子类都添加观察,在触发观察时:
willSet
方法,先子类后父类didSet
方法,先父类后子类