「这是我参加2022首次更文应战的第3天,活动详情检查:2022首次更文应战」。
- 本文首要介绍类的生命周期剖析
咱们知道在iOS中,app开发经过
编译器
把咱们的代码编译成机器可辨认的代码mach-o
文件,编译器分为前端编译器
和后端编译器
,oc中前端编译器为clang
,swift中为swift编译器
。经过前端编译器进行词法剖析
,语法剖析
,检查语法
是否正确生成中间代码IR
之后经过中间层进行代码优化
,交给后端llvm
进行处理,生成mach-o
文件
流程如下:
咱们接下来看下前端编译器swift的命令
,相似咱们oc中clang的命令句子
// 剖析输出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
1. SIL文件剖析
根据上面的指令咱们生成main的sil文件,咱们新建一个CommandLineTool
工程,在main界说一个Person类
sil
文件,并在终端打印生成的main.sil
文件
swiftc main.swift -emit-sil
上面的 person就是咱们的类,包括属性的get
和set
办法,以及deinit
和init
办法。@main是进口函数。
咱们能够经过> 在当前目录下生成main.sil
文件
swiftc main.swift -emit-sil > ./main.sil
// main 相当于oc中main的进口
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
alloc_global @$s4main1pAA6PersonCvp // id: %2 分配一个person的全局变量
%3 = global_addr @$s4main1pAA6PersonCvp : $*Person // user: %7 把person全局变量给%3寄存器
%4 = metatype $@thick Person.Type // user: %6 %4 赋值为Person.Type 的元类型
// function_ref Person.__allocating_init()//执行init办法函数
%5 = function_ref @$s4main6PersonCACycfC : $@convention(method) (@thick Person.Type) -> @owned Person // user: %6
//%5相当于办法函数的指针地址
%6 = apply %5(%4) : $@convention(method) (@thick Person.Type) -> @owned Person // user: %7
// 经过请求 init办法中传入Person.Type的类型 生成实例变量 赋值给%6
store %6 to %3 : $*Person // id: %7
//store 存储,把咱们实例的目标 咱们之前界说的全局变量%3中 只能*Person
%8 = integer_literal $Builtin.Int32, 0 // user: %9
%9 = struct $Int32 (%8 : $Builtin.Int32) // user: %10
//$Int32 值相当于 0 ,相似咱们oc中 main return 0
return %9 : $Int32 // id: %10
} // end sil function 'main'
上面的%0,%1
等是虚拟的寄存器
。在SIL文件中,你会看到许多不懂的关键词,你能够检查GitHub上的官方文档查阅
- 脚本编译翻开
咱们也能够增加脚本运转翻开
1.增加target
增加脚本,这里需求在当前main地点的文件夹
swiftc -emit-sil ${SRCROOT}/*项目文件夹*/main.swift | xcrun swift-demangle >
./main.sil && open main.sil
报错的话,或者说无法翻开,你能够先翻开vscode
,设置main.sil
的翻开方式
2 汇编剖析流程
咱们运用汇编进行剖析
__allocating_init()
处打断点
进入后会调用init办法进行初始化
进入init
3.源码剖析
接下来咱们来看一下源码。源码能够去苹果官网下-swift源码下载地址。用 VSCode 翻开下载好的 swift 源码,全局搜索swift_allocObject
这个函数。
咱们就像看下_swift_allocObject_
的实现,首要传3个参数,根据分配的内存巨细经过swift_slowAlloc
创建内存空间,之后把元数据关联内存空间,最终回来这个实例目标。
点击进入swift_slowAlloc
检查
对齐方式默许巨细
- HeapObject
首要有2个初始化的办法,都包括2个参数别离时HeapMetadata
类型的metaData
和InlineRefCounts
类型的refcounts
HeapMetadata
检查TargetHeapMetadata
兼容了oc中的类,数据元为isa
;swift类的话默许MetaDataKind
类型的数据
#include "MetadataKind.def"
得到对应的值
name Value
Class 0x0
Struct 0x200
Enum 0x201
Optional 0x202
ForeignClass 0x203
ForeignClass 0x203
Opaque 0x300
Tuple 0x301
Function 0x302
Existential 0x303
Metatype 0x304
ObjCClassWrapper 0x305
ExistentialMetatype 0x306
HeapLocalVariable 0x400
HeapGenericLocalVariable 0x500
ErrorObject 0x501
LastEnumerated 0x7FF
持续检查TargetHeapMetadata
承继的TargetMetadata
,在C++中结构体能够承继
检查TargetMetadata
里面有许多办法属性,咱们关注下getTypeContextDescriptor
的办法
根据MetaDataKind
的类型获取不同的des
,当kind
是一个Class
的时分,会拿到一个名为TargetClassMetadata
的指针,咱们看看TargetClassMetadata
的实现:
承继TargetAnyClassMetadata
,点击检查
如果是oc类,这个TargetAnyClassMetadata
这个结构和咱们oc中类
的结构相似isa
,superClass
,cache
,bits(data)。
所以咱们总结下swift的数据结构
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
}
前面咱们剖析可知初始化生成一个heapObject
的目标,咱们能够仿照这个结构体进行自界说
class Person{
var name = "fish"
var age = 3
}
struct HeapObject{
var metadata: UnsafeRawPointer
var refCounts: 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
}
let p = Person()
let objcRawPtr = Unmanaged.passUnretained(p as AnyObject).toOpaque()//获取实例目标的指针
let objcPtr = objcRawPtr.bindMemory(to: HeapObject.self
, capacity: 1)//把HeapObject转换咱们自界说的MetaData结构
print(objcPtr.pointee);
咱们运用lldb验证下
打印下metadata
结构体内存说明类实质是一个结构体
4.总结
- swift中的类实质是
heapObject
类型的结构体包括metadata
和引证计数
,我么oc中的类实质是objc_class
类型的结构体。 - swift中实例目标存贮的是
metadata
和引证计数
,成员变量
。oc中实例变量存储的是isa
和成员变量
- swift中
metadata
中包括类的信息经过getTypeContextDescriptor
获取描述的类型把数据存在对应的结构体中。 - 大致流程