为了探索swift类的本质,我们创建一个类并将文件编译,生成可读的SIL文件a
class Person {
var name = "dotry"
var age = 26
}
let p = Person()
编译之后得到
解析过程如下
- @main标识当前man.swift的入口函数,SIL中的 标识符名称以@作为前缀。
- %0,%1……在SIL中也叫做寄存器,可以理解为平时代码中定义的常量,一旦赋值之后就不可以改变。如果SIL中还要继续使用,只有不断地累加数字。同时这里的寄存器是虚拟的,最终运行到设备上使用的是真正的寄存器。
alloc_global
创建一个全局变量。global_addr
拿到全局变量的地址,赋值给%3。metatype
拿到Person
的Metadata
赋值给%4。- 将
__allocating_init
的函数地址赋值给%5。 apply
调用__allocating_init
,并把返回值赋值给%6。- 将%6赋值给刚才创建的全局变量%3。
- 构建
Int
,并return
。
我们跟踪里面最重要的函数部分__allocating_init,通过断点得到
然后再通过VSCode
调试看到了swift_allocObject
内部又调用了什么,如下图: 继续执行发现执行的方法链为
__allocating_init
--> swift_allocObject
--> _swift_allocObject_
--> swift_slowAlloc
--> malloc
我们得到以下结论:
- Swift对象的内存结构
HeapObject
有两个属性,一个是Metadata
,一个是RefCount
。所以一个Swift对象至少占用16个字节。 init
在这里和OC中一样,承担了初始化内存的职责。
探索Swift中类的结构
在OC中类是从objc_class
模板继承过来的,具体的参考这篇文章iOS-底层原理 08:类 & 类结构分析
而在Swift中,类的结构在底层是HeapObject
,其中有 metadata + refCounts
HeapMetadata类型分析
下面就来分析metadata,看看它到底是什么?
-
进入
HeapMetadata
定义,是TargetHeapMetaData
类型的别名,接收了一个参数Inprocess
using HeapMetadata = TargetHeapMetaData<Inprocess>;
-
进入
TargetHeapMetaData
定义,其本质是一个模板类型
,其中定义了一些所需的数据结构。这个结构体中没有属性,只有初始化
方法,传入了一个MetadataKind
类型的参数(该结构体没有,那么只有在父类中了)这里的kind
就是传入的Inprocess
//模板类型
template <typename Runtime>
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
};
-
进入
TargetMetaData
定义,有一个kind
属性,kind
的类型就是之前传入的Inprocess
。从这里可以得出,对于kind
,其类型就是unsigned long
,主要用于区分是哪种类型的元数据
//******** TargetMetaData 定义 ********
struct TargetMetaData{
using StoredPointer = typename Runtime: StoredPointer;
...
StoredPointer kind;
}
//******** Inprocess 定义 ********
struct Inprocess{
...
using StoredPointer = uintptr_t;
...
}
//******** uintptr_t 定义 ********
typedef unsigned long uintptr_t;
从TargetHeapMetadata、TargetMetaData
定义中,均可以看出初始化方法中参数kind
的类型是MetadataKind
-
进入
MetadataKind
定义,里面有一个#include "MetadataKind.def"
,点击进入,其中记录了所有类型的元数据
,所以kind
种类总结如下
name | value |
---|---|
Class | 0x0 |
Struct | 0x200 |
Enum | 0x201 |
Optional | 0x202 |
ForeignClass | 0x203 |
Opaque | 0x300 |
Tuple | 0x301 |
Function | 0x302 |
Existential | 0x303 |
Metatype | 0x304 |
ObjCClassWrapper | 0x305 |
ExistentialMetatype | 0x306 |
HeapLocalVariable | 0x400 |
HeapGenericLocalVariable | 0x500 |
ErrorObject | 0x501 |
LastEnumerated | 0x7FF |
-
回到
TargetMetaData
结构体定义中,找方法getClassObject
,在该方法中去匹配kind
返回值是TargetClassMetadata
类型-
如果是
Class
,则直接对this(当前指针,即metadata)强转为ClassMetadata
-
const TargetClassMetadata<Runtime> *getClassObject() const;
//******** 具体实现 ********
template<> inline const ClassMetadata *
Metadata::getClassObject() const {
//匹配kind
switch (getKind()) {
//如果kind是class
case MetadataKind::Class: {
// Native Swift class metadata is also the class object.
//将当前指针强转为ClassMetadata类型
return static_cast<const ClassMetadata *>(this);
}
case MetadataKind::ObjCClassWrapper: {
// Objective-C class objects are referenced by their Swift metadata wrapper.
auto wrapper = static_cast<const ObjCClassWrapperMetadata *>(this);
return wrapper->Class;
}
// Other kinds of types don't have class objects.
default:
return nullptr;
}
}
这一点,我们可以通过lldb
来验证
-
po metadata->getKind()
,得到其kind是Class -
po metadata->getClassObject()
、x/8g 0x0000000110efdc70,这个地址中存储的是元数据信息!源码分析-4
所以,TargetMetadata
和 TargetClassMetadata
本质上是一样的,因为在内存结构中,可以直接进行指针的转换
,所以可以说,我们认为的结构体
,其实就是TargetClassMetadata
-
进入
TargetClassMetadata
定义,继承自TargetAnyClassMetadata
,有以下这些属性,这也是类结构的部分
template <typename Runtime>
struct TargetClassMetadata : public TargetAnyClassMetadata<Runtime> {
...
//swift特有的标志
ClassFlags Flags;
//实力对象内存大小
uint32_t InstanceSize;
//实例对象内存对齐方式
uint16_t InstanceAlignMask;
//运行时保留字段
uint16_t Reserved;
//类的内存大小
uint32_t ClassSize;
//类的内存首地址
uint32_t ClassAddressPoint;
...
}
-
进入
TargetAnyClassMetadata
定义,继承自TargetHeapMetadata
template <typename Runtime>
struct TargetAnyClassMetadata : public TargetHeapMetadata<Runtime> {
...
ConstTargetMetadataPointer<Runtime, swift::TargetClassMetadata> Superclass;
TargetPointer<Runtime, void> CacheData[2];
StoredSize Data;
...
}
总结
综上所述,当metadata
的kind
为Class时,有如下继承链:
源码分析-5
-
当前类返回的实际类型是
TargetClassMetadata
,而TargetMetaData中只有一个属性kind
,TargetAnyClassMetaData
中有4个属性,分别是kind, superclass,cacheData、data(图中未标出)
-
当前
Class在内存中所存放的属性
由TargetClassMetadata
属性 +TargetAnyClassMetaData
属性 +TargetMetaData
属性 构成,所以得出的metadata的数据结构体如下所示
struct swift_class_t: NSObject{
void *kind;//相当于OC中的isa,kind的实际类型是unsigned long
void *superClass;
void *cacheData;
void *data;
uint32_t flags; //4字节
uint32_t instanceAddressOffset;//4字节
uint32_t instanceSize;//4字节
uint16_t instanceAlignMask;//2字节
uint16_t reserved;//2字节
uint32_t classSize;//4字节
uint32_t classAddressOffset;//4字节
void *description;
...
}
与OC对比
-
实例对象 & 类
-
OC中的
实例对象本质
是结构体
,是通过底层的objc_object
模板创建,类是继承自objc_class
-
Swift中的
实例对象本质
也是结构体
,类型是HeapObject
,比OC多了一个refCounts
-
-
方法列表
-
OC
中的方法存储在objc_class
结构体class_rw_t
的methodList
中 -
swift
中的方法存储在metadata
元数据中
-
-
引用计数
-
OC中的ARC维护的是
散列表
-
Swift中的ARC是对象内部有一个
refCounts
属性
-