Swift探索1-类与结构体 上

168 阅读8分钟

[TOC]

本文是swift探索的开始,主要内容分为三点
1.类与结构体 区别和联系
2.类初始化器规则,以便写出比较swift的代码
3.类的生命周期,对当前类 内存结构、数据结构 进行探索,并通过看swift的源码来理解

swift源码(github.com/apple/swift)

1. 初识类与结构体

1.1 struct和class区别 联系

举例

//结构体
struct LGTeacher{
    var age: Int
    var name: String

    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }
    
}
//类
class LGTeacher{
    var age: Int
    var name: String

    init(age: Int, name: String) {
        self.age = age
        self.name = name
    }
    
    deinit {
        
    }
}

类和结构体 的相同点:

  • 定义存储值得的属性
  • 定义方法
  • 定义下表以使用下标语法提供对其值的访问
  • 定义初始化器
  • 使用extension来拓展功能
  • 遵循协议来提供某种功能

不同点:

  • 类有继承的特性,而结构体没有

  • 类型转换是您能够在运行时检查和解释类示例的类型(反射源码)

  • 类有析构函数来释放其分配的资源

  • 引用计数允许对一个类实例有多个引用

1.1.1 重点分析:类是引用类型,结构体是值类型

类是引用类型
一个类类型的变量并不直接存储具体的实例对象,是对当前存储具体实例内存地址的引用。

一方面通过图解分析
var t -> LGTeacher(name: "lr")
var t1 = t
则 t1和t 都指向 LGTeacher(name: "lr")
var t
-> LGTeacher(name: "lr")
var t1

图片.png 也可以通过lldb命令来打印验证

补充lldb命令
po : ppo 的区别在于使用 po 只会输出对应的值,而 p 则会返回值的类型以及命令结果 的引用名。
x/8g: 读取内存中的值(8g: 8字节格式输出)
var t = LGTeacher(age: 18, name: "lr")
var t1 = t
t1.age = 20

打印结果

(lldb) po t
<LGTeacher: 0x100742dd0>

(lldb) po t1
<LGTeacher: 0x100742dd0>

(lldb) x/8g 0x100742dd0
0x100742dd0: 0x0000000100008218 0x0000000600000003
0x100742de0: 0x0000000000000012 0x000000000000726c
0x100742df0: 0xe200000000000000 0x0000000000000000
0x100742e00: 0x000000000000000a 0x00007ff9294e804c

//打印指针t t1存储的地址
(lldb) po withUnsafePointer(to: &t, {print($0)})
0x0000000100008370
0 elements
(lldb) po withUnsafePointer(to: &t1, {print($0)})
0x0000000100008378
0 elements
//相差8个字节 ,刚好存储实例对象的内存地址 0x100742dd0

结构体是值类型
值类型,最典型的就是 Struct ,结构体的定义也非常简单,相比较 类类型的变量中存储的是地址,那么值类型存储的就是具体的实例(或者说具体的值)。

struct LGStudent{
    var age: Int
    var name: String
}
var s = LGStudent(age:18, name:"lr")
var s1 = s

//s1.age = 20

打印s、s1内容

(lldb) po s
 LGStudent
  - age : 18
  - name : "lr"

(lldb) po s1
 LGStudent
  - age : 18
  - name : "lr"

//即使修改s1.age = 20 也不会影响s的值
(lldb) po s1
 LGStudent
  - age : 20
  - name : "lr"

s = LGStudent(age:18, name:"lr")
s1 = LGStudent(age:18, name:"lr")
两个变量存储的 值 是相互独立的
图片.png

1.2 引用类型和值类型的区别

1.2.1 直观区别 存储位置不同

引用类型 一般存储在堆上
值类型 一般存储栈上

拓展内存知识
内存分布图
图片.png

举例

func test(){
    //我们在函数内部声明的age变量是不是就是一个局部变量
    var age: Int = 10
    print(age)
}
test()

打印地址验证

(lldb) po withUnsafePointer(to: &age, {print($0)})
0x00007ff7bfeff318
0 elements

(lldb) cat address 0x00007ff7bfeff318
address:0x00007ff7bfeff318, stack address (SP: 0x7ff7bfeff2f0 FP: 0x7ff7bfeff320) LGSwiftTest.test() -> ()
//确实是在堆区

栈:局部变量、参数、函数上下文
堆:存储所有对象
global:存储全局变量、常量、代码区

Segment & Section: Mach-O 文件有多个段( Segment ),每个段有不同的功能。然后每个段又分为很多小的 Section

TEXT.text : 机器码
TEXT.cstring : 硬编码的字符串
TEXT.const: 初始化过的常量
DATA.data: 初始化过的可变的(静态/全局)数据 
DATA.const: 没有初始化过的常量
DATA.bss: 没有初始化的(静态/全局)变量
DATA.common: 没有初始化过的符号声明

int a = 10;//DATA.data: 初始化过的可变的(静态/全局)数据
int age;  //DATA.bss: 没有初始化的(静态/全局)变量

int main() {
    char *p = "rr";//TEXT.cstring : 硬编码的字符串
}

通过 po &a
cat address
等方法验证对应变量在内存中的位置 对应的section

堆内存的消耗
引用计数的内存消耗

1.2.2 值类型(结构体)内存分配过程

使用命令查看变量

frame varibale -L xxx
查看变量内存地址信息
   -L ( --location )
        Show variable location information.
//结构体 在栈上的内存分配
struct LGTeacher{
   var age = 18
   var name = "lr"
}
func test(){
   var t = LGTeacher()
   print("end")
}
test()

打印变量内存地址信息

(lldb) frame variable -L t
0x00007ff7bfeff300: (LGSwiftTest.LGTeacher) t = {
0x00007ff7bfeff300:   age = 18
0x00007ff7bfeff308:   name = "lr"
}

栈区变量 var t = LGTeacher() 执行时
编译器首先会移动栈指针,在内存中分配24字节的内存大小
执行过test()方法之后,移动栈指针 还原到之前位置 销毁栈内存

值类型中有引用类型,不会改变内存分配的方式(结构体的存储位置)

//值类型中有引用类型
class LGPerson{
   var age = 18
   var name = "LGMan"
}
struct LGTeacher{
   var age = 18
   var name = "lr"
   var t = LGPerson()
}
func test(){
   var t = LGTeacher()
   print("end")
}
test()
(lldb) frame variable -L t
0x00007ff7bfeff300: (LGSwiftTest.LGTeacher) t = {
0x00007ff7bfeff300:   age = 18
0x00007ff7bfeff308:   name = "lr"
scalar:   p = 0x0000000100759b20 {
0x0000000100759b30:     age = 18
0x0000000100759b38:     name = "LGMan"
 }
}
//t还是在栈空间上, p也在栈空间上 , p指向的内存在堆上

t还是在栈空间上, p也在栈空间上 , p指向的内存在堆上

1.2.3 引用类型内存分配过程

class LGTeacher{
   var age = 18
   var name = "lr"
}
func test(){
   //栈上分配8字节
   //堆空间上寻找合适的内存区域
   //将value拷贝到堆上
   var t = LGTeacher()
   print("end")
}
test()

实例对象创建过程 内存分配

·栈上分配8字节  
·堆空间上寻找合适的内存区
·将value拷贝到堆上

销毁过程

·查找并将内存块重新插入堆空间中,对堆空间来说始终有一个查找的过程
·销毁栈上的指针

1.2.4 栈和堆在分配内存的过程中有时间和速度上的区别

官方案例分析
这里我们也可以通过github上StructVsClassPerformance(github.com/knguyen2708…) 这个案例来直观的测试当前结构体和类的时间分配。

聊天气泡案例

enum Color { case blue, green, gray }
enum Orientation { case left, right }
enum Tail { case none, tail, bubble }
var cache = [String : UIImage]()
func makeBalloon(_ color: Color, orientation: Orientation, tail: Tail) -> UIImage {
    let key = "\(color):\(orientation):\(tail)"
    if let image = cache[key] {
        return image
        
    }
    ...
}

cache[key] 会有string不停地进行堆内存分配和销毁,影响速度和效率, 如果优化卡顿,string当做key值是影响性能的,可以从此处着手

enum Color { case blue, green, gray }
enum Orientation { case left, right }
enum Tail { case none, tail, bubble }
var cache = [String : UIImage]()
func makeBalloon(_ balloon: Ballon) -> UIImage {
    if let image = cache[balloon] {
        return image
        
    }
    ...
}
struct Balloon: Hashable{
    var color: Color
    var orientation: Orientation
    var tail: Tail
}

避免了堆区内存分配,提高了效率

另一案例 附件上传

struct Attachment {
    let fileURL: URL
    let uuid: String
    let mineType: String
    init?(fileURL: URL, uuid: String, mimeType: String) {
        guard mineType.isMineType
        else { return nil }
        self.fileURL = fileURL
        self.uuid = uuid
        self.mineType = mimeType
    }
}

对string类型变量进行优化 let uuid: String let mineType: String

struct Attachment {
    let fileURL: URL
    let uuid: UUID
    let mineType: MimeType
    init?(fileURL: URL, uuid: String, mimeType: String) {
        guard mineType.isMineType
        else { return nil }
        self.fileURL = fileURL
        self.uuid = uuid
        self.mineType = mimeType
    }
}
enum MimeType: String{
    case jpeg = "image/jpeg"
    ....
}

同样也是避免了堆区内存分配,提高了效率

尽可能优先使用结构体(值类型),再根据业务类型决定是否需要类 值类型struct 在栈上,线程安全,内存分配速度快

2. 类的初始化器

2.1 成员初始化器

编译器会自动为 结构体 创建初始化器
但是对于 成员初始化器
当前的类编译器默认不会自动提供成员初始化器,但是对于结构体来说编译
器会提供默认的初始化方法(前提是我们自己没有指定初始化器)!

struct LGTeacher{ 
    var age: Int 
    var name: String
}

初始化器 规则 ,保证类型安全
保证self及其属性已经被初始化了

Swift 中创建类和结构体的实例时必须为所有的存储属性设置一个合适的初始值。
类 LGPerson 必须要提供对应的指定初始化器,同时我们也可以为当前的类提供便捷初始化器(注意:便捷初始化器必须从相同的类里调用另一个初始化器)。

2.2 指定初始化器

默认只有一个

2.3 便捷初始化器

必须调用同类中的其他初始化器

class LGPerson{ 
    var age: Int
    var name: String
    init(_ age: Int, _ name: String) {
        self.age = age
        self.name = name 
    }
    convenience init() { 
        self.init(age: 18, name:"lr")
    } 
}

当我们派生出一个子类 LGTeacher ,并指定一个指定初始化器之后会出现什么问题

class LGPerson{ 
    var age: Int
    var name: String
    init(_ age: Int, _ name: String) {
        self.age = age
        self.name = name 
    }
    convenience init() { 
        self.init(age: 18, name:"lr")
    } 
}
class LGTeacher: LGPerson{ 
    var subjectName: String
    init(subjectName: String){ 
        //指定初始化器必须保证在向上委托给父类初始化器之前,其所在类引入的所有属性都要初始化完成。
        self.subjectName = subjectName
        super.init(age: 18, name:"lr")
    }
}

初始化器的原则

  • 指定初始化器必须保证在向上委托给父类初始化器之前,其所在类引入的所有属性都要初始化完成。
  • 指定初始化器必须先向上委托父类初始化器,然后才能为继承的属性设置新值。如果不这样做,指定初始化器赋予的新值将被父类中的初始化器所覆盖
  • 便捷初始化器必须先委托同类中的其它初始化器,然后再为任意属性赋新值(包括 同类里定义的属性)。如果没这么做,便捷构初始化器赋予的新值将被自己类中其它指定初始化器所覆盖。
  • 初始化器在第一阶段初始化完成之前,不能调用任何实例方法、不能读取任何实例属性的值,也不能引用 self 作为值。

2.4 可失败初始化器

当前因为参数的不合法或者外部条件 的不满足,存在初始化失败的情况。这种 Swift 中可失败初始化器写 return nil 语句,来表明可失败初始化器在何种情况下会触发初始化失败。

class LGPerson{ 
    var age: Int
    var name: String
    init(_ age: Int, _ name: String) {
        if age < 18 {return nil}//可失败初始化器
        self.age = age
        self.name = name 
    }
    convenience init() { 
        self.init(age: 18, name:"lr")
    } 
}

2.5 必要初始化器

必要初始化器:在类的初始化器前添加 required 修饰符来表明所有该类的子类都必须 实现该初始化器

class LGPerson{ 
    var age: Int
    var name: String
    required init(_ age: Int, _ name: String) {//required
        self.age = age
        self.name = name 
    }
    convenience init() { 
        self.init(age: 18, name:"lr")
    } 
}
class LGTeacher: LGPerson{ 
    var subjectName: String
    init(subjectName: String){
        self.subjectName = subjectName
        super.init(age: 18, name:"lr")
    }
    convenience init() { 
        self.init(age: 18, name:"lr")
    } 
    //子类没有提供required init(_ age: Int, _ name: String)必要初始化器的实现 就会报错
}

3. 类的生命周期

前面的非常简单的基础知识 为现在做铺垫
图片.png iOS开发的语言不管是OC还是Swift 后端 都是通过LLVM进行编译的
OC 通过 clang 编译器,编译成 IR,然后再生成可执行文件 .o(这里也就是我们的机器码)
Swift 则是通过 Swift 编译器编译成 IR,然后在生成可执行文件。

3.1 swift代码编译过程

图片.png

        AST抽象语法树

Swift Code -> Parse -> Sema -> SILGen -> Raw SIL -> SILOpt Canonical SIL
-> IRGen -> LLVM IR -> Machine Code

// 分析输出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

例如C中

int8_t x = 100;
int8_t y = x + 100;
NSLog(@"%d",y);

结果-56 image.png int8_t是1字节 0-2^8 即-128-127 上面的结果已经越界

swift中

let x = Int8(100) + 100

image.png

swift类型安全 编译检期间就检查 sil swift中间语言进行的操作

main.swift文件

class LGTeacher{
    var age = 18
    var name = "lr"
}
var t = LGTeacher()

把main.swift文件编译成 .sil文件 并打开.sil文件

// 生成中间体语言(SIL),优化后的 
swiftc main.swift -emit-sil

//使用该脚本命令生成sil文件并打开
swiftc -emit-sil ${SRCROOT}/LGSwiftTest/main.swift > ./main.sil && open main.sil

3.2 分析swift类的初始化的流程

主要代码

图片.png 主要sil代码段分析

//标识有初始化过的存储属性  age name
class LGTeacher {
  @_hasStorage @_hasInitialValue var age: Int { get set }
  @_hasStorage @_hasInitialValue var name: String { get set }
  @objc deinit
  init()
}

@_hasStorage @_hasInitialValue var t: LGTeacher { get set }

// t
sil_global hidden @$s4main1tAA9LGTeacherCvp : $LGTeacher

// @main 入口函数 
// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
  alloc_global @$s4main1tAA9LGTeacherCvp          // id: %2 分配全局变量
  %3 = global_addr @$s4main1tAA9LGTeacherCvp : $*LGTeacher // user: %7 拿到全局变量的地址给%3
  %4 = metatype $@thick LGTeacher.Type            // user: %6  获取LGTeacher.Type的元类型
  
  // 函数引用  即 %5 是拿到函数LGTeacher.__allocating_init()的指针地址
  // %6 = 使用LGTeacher.__allocating_init() 并将元类型传入  产生实例变量
  // function_ref LGTeacher.__allocating_init()
  %5 = function_ref @$s4main9LGTeacherCACycfC : $@convention(method) (@thick LGTeacher.Type) -> @owned LGTeacher // user: %6
  %6 = apply %5(%4) : $@convention(method) (@thick LGTeacher.Type) -> @owned LGTeacher // user: %7
  
  // 把实例变量的内存地址存放到全局变量的地址里面
  store %6 to %3 : $*LGTeacher                    // id: %7
  
  //Int 结构体类型
  %8 = integer_literal $Builtin.Int32, 0          // user: %9
  %9 = struct $Int32 (%8 : $Builtin.Int32)        // user: %10
  return %9 : $Int32                              // id: %10
} // end sil function 'main'
 

@main: 入口函数, @ 一般做标识
%0: 寄存器,虚拟的,跑在设备上会使用真的寄存器 一旦赋值不可更改
xcrun swift-demangle 还原混写之后的名称

s4main1tAA9LGTeacherCvp 经过特殊的混写规则 混写过后的名称
终端执行 xcrun swift-demangle s4main1tAA9LGTeacherCvp 即可得到混写前的名称
终端执行

xcrun swift-demangle s4main1tAA9LGTeacherCvp
得结果
$s4main1tAA9LGTeacherCvp ---> main.t : main.LGTeacher

函数指针 s4main9LGTeacherCACycfC 即 LGTeacher.__allocating_init()的调用过程sil代码

// LGTeacher.__allocating_init()
sil hidden [exact_self_class] @$s4main9LGTeacherCACycfC : $@convention(method) (@thick LGTeacher.Type) -> @owned LGTeacher {
// %0 "$metatype"
bb0(%0 : $@thick LGTeacher.Type):
  %1 = alloc_ref $LGTeacher                       // user: %3
  // function_ref LGTeacher.init()
  %2 = function_ref @$s4main9LGTeacherCACycfc : $@convention(method) (@owned LGTeacher) -> @owned LGTeacher // user: %3
  %3 = apply %2(%1) : $@convention(method) (@owned LGTeacher) -> @owned LGTeacher // user: %4
  return %3 : $LGTeacher                          // id: %4
} // end sil function '$s4main9LGTeacherCACycfC'

LGTeacher.Type 元类型 可以理解为isa类型 alloc_ref 方法等语法可以查看 sil语法地址(github.com/apple/swift/bolb/main/docs/SIL.rst)

image.png 创建T的实例对象,引用计数初始化为1,即alloc_ref 是去堆区申请内存空间;
如果标识为objc的 会使用OC的初始化方法

在代码里面验证

//swift情况
class LGTeacher{
    var age = 18
    var name = "lr"
}
var t = LGTeacher()

//OC情况
class LGTeacher : NSObject {
    var age = 18
    var name = "lr"
}
var t = LGTeacher()

加符号断点 __allocating_init

swift情况 image.png

step into
image.png 可以猜测 swift_allocObject 在堆区找到合适的内存空间
init()初始化所有的成员变量
swift对象分配内存的流程
__allocating_init -> swift_allocObject -> init()

OC情况 image.png

step into image.png

objc_allocWithZone 初始化函数 通过malloc函数在 堆区分配内存
objc_msgSend 发送消息 init 这个函数

继续swift的探索,通过swift源码查看 (github.com/apple/swift) swift_allocObject 的实现过程

HeapObject.cpp文件

图片.png swift_allocObject -> swift_allocObject

static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,
                                       size_t requiredSize,
                                       size_t requiredAlignmentMask) {
  assert(isAlignmentMask(requiredAlignmentMask));
  auto object = reinterpret_cast<HeapObject *>(
      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.
  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 *swift::swift_allocObject(HeapMetadata const *metadata,
                                     size_t requiredSize,
                                     size_t requiredAlignmentMask) {
  CALL_IMPL(swift_allocObject, (metadata, requiredSize, requiredAlignmentMask));
}

通过源码可以继续发现swift对象内存分配调用流程

__allocating_init() -> swift_allocObject -> _swift_allocObject_ -> swift_slowAlloc  

HeapObject.cpp文件

swift_allocObject -> _swift_allocObject_ -> swift_slowAlloc

swift_slowAlloc 的实现

图片.png

void *swift::swift_slowAlloc(size_t size, size_t alignMask) {
  void *p;
  // This check also forces "default" alignment to use AlignedAlloc.
  if (alignMask <= MALLOC_ALIGN_MASK) {
#if defined(__APPLE__) && SWIFT_STDLIB_HAS_DARWIN_LIBMALLOC
    p = malloc_zone_malloc(DEFAULT_ZONE(), size);
#else
    p = malloc(size);
#endif
  } else {
    size_t alignment = (alignMask == ~(size_t(0)))
                           ? _swift_MinAllocationAlignment
                           : alignMask + 1;
    p = AlignedAlloc(size, alignment);
  }
  if (!p) swift::crash("Could not allocate memory.");
  return p;
}

至此可以总结出swift对象内存分配方法的调用过程

__allocating_init -> swift_allocObject -> _swift_allocObject_ -> swift_slowAlloc -> malloc

3.3 swift对象的内存结构

Swift 对象的内存结构 HeapObject (OC objc_object) ,
是HeapObject 类型,查看HeapObject源码

图片.pngnew (object) HeapObject(metadata); 的实现

struct HeapObject {
  /// This is always a valid pointer to a metadata object.
  HeapMetadata const *__ptrauth_objc_isa_pointer metadata;

  SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;

#ifndef __swift__
  HeapObject() = default;

  // Initialize a HeapObject header as appropriate for a newly-allocated object.
  constexpr HeapObject(HeapMetadata const *newMetadata) 
    : metadata(newMetadata)                   //8字节
    , refCounts(InlineRefCounts::Initialized) //64位位域信息 8字节
  { }
  
  // Initialize a HeapObject header for an immortal object
  constexpr HeapObject(HeapMetadata const *newMetadata,
                       InlineRefCounts::Immortal_t immortal)
  : metadata(newMetadata)
  , refCounts(InlineRefCounts::Immortal)
  { }

#ifndef NDEBUG
  void dump() const SWIFT_USED;
#endif

有两个属性:

一个是 Metadata ,8字节 相当于isa
一个是 RefCount ,默认占用 16 字节大小。 64位的位域信息
  constexpr HeapObject(HeapMetadata const *newMetadata) 
    : metadata(newMetadata)                   //8字节
    , refCounts(InlineRefCounts::Initialized) //64位位域信息 8字节
  { }

相比OC 对象内存结构 只有isa 8字节

objc_object{
    isa
}

分析 metadata 的内存结构, 类似分析isa, metadata是HeapMetadata 类型, 则查找HeapMetadata类型定义,点进去

图片.png

图片.png

template <typename Target> struct TargetHeapMetadata;
using HeapMetadata = TargetHeapMetadata<InProcess>;
#else
typedef struct HeapMetadata HeapMetadata;
typedef struct HeapObject HeapObject;
#endif
struct TargetHeapMetadata : TargetMetadata<Runtime> {
  using HeaderType = TargetHeapMetadataHeader<Runtime>;

  TargetHeapMetadata() = default;
  constexpr TargetHeapMetadata(MetadataKind kind)
    : TargetMetadata<Runtime>(kind) {}
#if SWIFT_OBJC_INTEROP
  constexpr TargetHeapMetadata(TargetAnyClassMetadata<Runtime> *isa)
    : TargetMetadata<Runtime>(isa) {}
#endif
};
using HeapMetadata = TargetHeapMetadata<InProcess>;

如果是纯swift类 则是 MetadataKind kind 类型 如果和objc类交互SWIFT_OBJC_INTEROP 则是 isa 类型

至于MetadataKind kind类型 图片.png

/// Kinds of Swift metadata records.  Some of these are types, some
/// aren't.
enum class MetadataKind : uint32_t {
#define METADATAKIND(name, value) name = value,
#define ABSTRACTMETADATAKIND(name, start, end)                                 \
  name##_Start = start, name##_End = end,
#include "MetadataKind.def"

MetadataKind 是 uint32_t 类型

OC中类的结构: objc_class

根据 objc_class 的结构进行猜想查找源码中对于swift类的结构的定义
在Metadata.h中找到如下代码

/// Get the nominal type descriptor if this metadata describes a nominal type,
  /// or return null if it does not.
  ConstTargetMetadataPointer<Runtime, TargetTypeContextDescriptor>
  getTypeContextDescriptor() const {
    switch (getKind()) {
    case MetadataKind::Class: {
      const auto cls = static_cast<const TargetClassMetadata<Runtime> *>(this);
      if (!cls->isTypeMetadata())
        return nullptr;
      if (cls->isArtificialSubclass())
        return nullptr;
      return cls->getDescription();
    }
    case MetadataKind::Struct:
    case MetadataKind::Enum:
    case MetadataKind::Optional:
      return static_cast<const TargetValueMetadata<Runtime> *>(this)
          ->Description;
    case MetadataKind::ForeignClass:
      return static_cast<const TargetForeignClassMetadata<Runtime> *>(this)
          ->Description;
    default:
      return nullptr;
    }
  }
case MetadataKind::Class: {
  const auto cls = static_cast<const TargetClassMetadata<Runtime> *>(this); 
  

当kind类型是 Class 类型时,将this指针强转(static_cast)成TargetClassMetadata类型 则 查看 TargetClassMetadata 代码

图片.png

template <typename Runtime>
struct TargetClassMetadata : public TargetAnyClassMetadata<Runtime> {
  using StoredPointer = typename Runtime::StoredPointer;
  using StoredSize = typename Runtime::StoredSize;

  TargetClassMetadata() = default;
  constexpr TargetClassMetadata(const TargetAnyClassMetadata<Runtime> &base,
             ClassFlags flags,
             ClassIVarDestroyer *ivarDestroyer,
             StoredPointer size, StoredPointer addressPoint,
             StoredPointer alignMask,
             StoredPointer classSize, StoredPointer classAddressPoint)
    : TargetAnyClassMetadata<Runtime>(base),
      Flags(flags), InstanceAddressPoint(addressPoint),
      InstanceSize(size), InstanceAlignMask(alignMask),
      Reserved(0), ClassSize(classSize), ClassAddressPoint(classAddressPoint),
      Description(nullptr), IVarDestroyer(ivarDestroyer) {}

  // The remaining fields are valid only when isTypeMetadata().
  // The Objective-C runtime knows the offsets to some of these fields.
  // Be careful when accessing them.

  /// Swift-specific class flags.
  ClassFlags Flags;

  /// The address point of instances of this type.
  uint32_t InstanceAddressPoint;

  /// The required size of instances of this type.
  /// 'InstanceAddressPoint' bytes go before the address point;
  /// 'InstanceSize - InstanceAddressPoint' bytes go after it.
  uint32_t InstanceSize;

  /// The alignment mask of the address point of instances of this type.
  uint16_t InstanceAlignMask;
  ...
  

这个代码就和objc_class非常像了

3.4 结构体绑定指针类型 验证结构猜想

猜想对象的数据结构 和 元类型的数据结构

//实例对象的结构体
struct HeapObject{
    var metadata: UnsafeRawPointer
    var refcounted1: UInt32
    var refcounted2: UInt32
}
//类的结构体
struct Metadata{
    var kind: Int
    var superClass: Any.Type
    var cacheData: (Int, Int)
    var data: Int
    var classFlags: Int32
    var instanceAddressPoint: UInt32
    var instanceSize: UInt32
    var instanceAlignmentMask: UInt16
    var reserved: UInt16
    var classSize: UInt32
    var classAddressPoint: UInt32
    var typeDescriptor: UnsafeMutableRawPointer
    var iVarDestroyer: UnsafeRawPointer
}

加入测试代码

class LGTeacher {
    var age = 18
    var name = "lr"
}
var t = LGTeacher()

//将实例对象的内存指针 还原成了 自定义的结构体
let objcRawPtr = Unmanaged.passUnretained(t as AnyObject).toOpaque() //获取对象的指针 原生指针
let objcPtr = objcRawPtr.bindMemory(to: HeapObject.self, capacity: 1) //绑定内存
print(objcPtr.pointee)

//objcPtr.pointee.metadata 元类型的函数指针
//把元类型指针 绑定成 Metadata 结构体类型
let metadata = objcPtr.pointee.metadata.bindMemory(to: Metadata.self, capacity: MemoryLayout<Metadata>.stride).pointee

print(metadata)

//通过以上打印证明 Metadata 就是猜想的数据结构
print("end")

//打印结果
HeapObject(metadata: 0x0000000100008240, refcounted1: 3, refcounted2: 0)
Metadata(kind: 4295000584, superClass: _TtCs12_SwiftObject, cacheData: (140703658047488, 140943646785536), data: 4302345010, classFlags: 2, instanceAddressPoint: 0, instanceSize: 40, instanceAlignmentMask: 7, reserved: 0, classSize: 168, classAddressPoint: 16, typeDescriptor: 0x0000000100003bec, iVarDestroyer: 0x0000000000000000)
end

通过以上打印证明 Metadata 就是猜想的数据结构 通过指针绑定的方式证明了猜想的 实例对象结构体HeapObject 和 类的结构体Metadata 就是要找的数据结构