编译流程
OC是通过Clang编译器编译成IR,然后再生成可执行文件.oSwift是通过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方法,先父类后子类