Swift基础-类、对象、属性

149 阅读4分钟

编译流程

image.png

  • OC是通过Clang编译器编译成IR,然后再生成可执行文件.o
  • Swift是通过swiftc编译器编译生成IR,然后再生成可执行文件.o

Swift编译流程图

image.png 主要分为以下几个过程:

    1. Parsing(解析):解析器是一个简单的递归下降解析器(在 lib/Parse 中实现),带有一个集成的、手工编码的词法分析器。解析器负责生成没有任何语义或类型信息的抽象语法树(AST),并针对输入源的语法问题发出警告或错误
    1. Semantic analysis(语义分析):语义分析(在 lib/Sema 中实现)负责获取解析后的AST并将其转换为格式良好、经过完全类型检查的AST形式,针对源代码中的语义问题发出警告或错误。语义分析包括类型推断,如果成功,则表明从生成的、类型检查的AST生成代码是安全的
    1. Clang importer(Clang导入器)Clang导入器(在 lib/ClangImporter 中实现)导入Clang并将它们导出的CObjective-C API映射到相应的Swift API。生成的导入AST可以通过语义分析进行引用
    1. SIL generation(SIL生成)Swift中间语言(SIL)是一种高级的、特定于Swift的中间语言,适用于进一步分析和优化Swift代码。SIL生成阶段(在 lib/SILGen 中实现)将经过类型检查的AST降低为所谓的“原始”SILSIL的设计在docs/SIL.rst 中进行了描述
    1. SIL guaranteed transformations(SIL保证转换)SIL保证转换(在 lib/SILOptimizer/Mandatory 中实现)执行影响程序正确性的附加数据流诊断(例如使用未初始化的变量)。这些转换的最终结果是“规范的”SIL
    1. SIL Optimizations(SIL优化)SIL优化(在 lib/Analysislib/ARClib/LoopTransforms和 lib/Transforms 中实现)对程序执行额外的高级、特定于Swift的优化,包括(例如)自动引用计数优化,去虚拟化和泛型专业化
    1. LLVM IR Generation(LLVM IR 生成)IR生成(在 lib/IRGen 中实现)将SIL 降低到LLVM IR,此时LLVM可以继续优化它并生成机器代码。

生成SIL

  • 指令: swiftc -emit-sil main.swift >> ./main.silswift文件生成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字节`

总结:

    1. 对象内存分配流程:__allocating_init -> swift_allocObject_ -> swift_slowAlloc -> malloc
    1. Swift实例对象占用16字节,比OC中多了refCounted(引用计数大小)

metadatakindClass

image.png

属性

存储属性

常量存储属性(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方法,变量存储属性可以settergetter方法

计算属性

  • 计算属性不占用内存,它本身不存储值,通过getter间接访问值
  • 计算属性没有存储属性拥有的_hasStorage @_hasInitialValue标识符

延迟属性

  • 延迟存储属性的初始值在其第⼀次使⽤时才能访问(在访问getter时,才会对age赋值),使用关键字lazy来标识一个延迟属性,延迟属性必须要设置一个初始值
  • 结果发现使用延迟属性后会导致内存增大

类型属性

  • 类型属性属于这个类的本身,不管有多少个实例,类型属性只有⼀份,我们可以使⽤static来声明⼀个类型属性
  • 这个属性本质是调用 swift_once函数,而其中是调用了gcd的dispatch_once_f单例

属性观察者(willSet/didSet)

    1. 属性观察可以添加在类的存储属性继承的存储属性继承的计算属性
    1. 父类在调用init中改变属性值不会触发属性观察,子类调用父类的init触发属性观察
    1. 统一属性在父类和子类都添加观察,在触发观察时:
    • willSet方法,先子类后父类
    • didSet方法,先父类后子类