前言
本文将会初次探索Swift
底层,但随着Swift
版本更新,底层结构可能会变动(ABI
已经稳定,即使调整,应该也是微调),所以在这边记录下版本号。
Swift
源码版本是5.3.1
,Xcode
版本是12.3
(其实对应的源码版本是5.3.2
,不过小版本,懒的更新了)
本文会初步探索Metadata
,详细的底层结构会在文章末尾附上链接。
由于比较深入底层,会有较多指针类型,如果你不是很熟悉Swift
类型的指针,可以先看下我上一篇写的文章。
通过Mirror
获取类型的Metadata
虽然Swift
重心在强调静态类型上,但它通过反射Mirror
的API
,允许代码在运行时检查和操作任意值。这边我们通过Mirror
的源码,看如何获取数据类型的元数据Metadata
的,我们先以struct
为突入口。
我们在源码中运行如下代码
struct Teacher {var age = 18; var name = "kody"}
let t = Teacher.init()
let mirror = Mirror(reflecting: t)
在初始化mirror
前,我们在init(reflecting subject: Any)
方法中打上断点,随着断点走:
我们可以看到这些框起来的方法(获取类型,及属性个数)都调用了一个call
,然后拿到了一个impl
变量,然后返回impl
的某个属性就可以了,所以这边call
方法和impl
是什么比较关键,我们继续往下走:
先进入call
方法(部分截图,太长了):
初步看到impl
是ReflectionMirrorImpl
,这里是一个类似闭包的东西,参数传了一个ReflectionMirrorImpl
类型,接着往下走,看看谁会调用:
我们发现通过调用type->getKind()
获取数据类型,发现是一个结构体,然后进入switch
的结构体的分支,发现impl
变成了StructImpl
,我们搜索StructImpl
可以看到
从图中我们可以发现:StructImpl
是ReflectionMirrorImpl
的子类,Struct
的元数据Metadata
是StructMetadata
。
简单的整理下Mirror
底层的核心逻辑,就是通过getKind()
方法获取该类型的元数据类型,然后根据该类型Metadata
获取相应的属性,比如类型名称、属性名字,属性个数等
MetadataKind
前面在type->getKind()
在switch
中,我们看到了MetadataKind::Struct
这个类型。
我们看下MetadataKind
类型:
enum class MetadataKind : uint32_t
MetadataKind
是一个Int32
大小的枚举,我们看下MetadataKind::Struct
系列的对应的枚举值
const unsigned MetadataKindIsNonType = 0x400;
const unsigned MetadataKindIsNonHeap = 0x200;
const unsigned MetadataKindIsRuntimePrivate = 0x100;
LastEnumerated = 0x7FF,
NOMINALTYPEMETADATAKIND(Class, 0)
NOMINALTYPEMETADATAKIND(Struct, 0 | MetadataKindIsNonHeap)
NOMINALTYPEMETADATAKIND(Enum, 1 | MetadataKindIsNonHeap)
NOMINALTYPEMETADATAKIND(Optional, 2 | MetadataKindIsNonHeap)
METADATAKIND(ForeignClass, 3 | MetadataKindIsNonHeap)
METADATAKIND(Opaque, 0 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
METADATAKIND(Tuple, 1 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
METADATAKIND(Function, 2 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
METADATAKIND(Existential, 3 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
METADATAKIND(Metatype, 4 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
METADATAKIND(ObjCClassWrapper, 5 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
METADATAKIND(ExistentialMetatype, 6 | MetadataKindIsRuntimePrivate | MetadataKindIsNonHeap)
METADATAKIND(HeapLocalVariable, 0 | MetadataKindIsNonType)
METADATAKIND(HeapGenericLocalVariable, 0 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate)
METADATAKIND(ErrorObject, 1 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate)
我们可以把Kind
整理成一张表格
名称 | 枚举值 | 说明 |
---|---|---|
Class | 0x0 | 类 |
Struct | 0x200 | 结构体 |
Enum | 0x201 | 枚举 |
Optional | 0x202 | 可选类型 |
ForeignClass | 0x203 | 外部类,比如CoreFoundation中的类 |
Opaque | 0x300 | 在元数据系统中不公开其值的类型 |
Tuple | 0x301 | 元祖类型 |
Function | 0x302 | A monomorphic function |
Existential | 0x303 | An existential type |
Metatype | 0x304 | A metatype |
ObjCClassWrapper | 0x305 | An ObjC class wrapper |
ExistentialMetatype | 0x306 | An existential metatype |
HeapLocalVariable | 0x400 | 使用静态生成的元数据的堆分配的局部变量 |
HeapGenericLocalVariable | 0x500 | 使用运行时实例化的元数据的堆分配的局部变量 |
ErrorObject | 0x501 | swift原生的错误类型 |
LastEnumerated | 0x7FF | 最大的非isa指针元数据类型值 |
所以我们前面type
存的是0x200
,对应的枚举值是结构体,我们可以从源码的断点处可以看到,我们后面也会用代码实现一遍。
StructMetadata
TargetStructMetadata
StructMetadata
是什么呢?我们可以全局搜索下,可以找到
using StructMetadata = TargetStructMetadata<InProcess>;
StructMetadata
就是TargetStructMetadata
的别名
/// The structure of type metadata for structs.
template <typename Runtime>
struct TargetStructMetadata : public TargetValueMetadata<Runtime> {
using StoredPointer = typename Runtime::StoredPointer;
using TargetValueMetadata<Runtime>::TargetValueMetadata;
const TargetStructDescriptor<Runtime> *getDescription() const {
return llvm::cast<TargetStructDescriptor<Runtime>>(this->Description);
}
...
};
我们没有找到有什么属性,我们看下他的父类TargetValueMetadata
struct TargetValueMetadata : public TargetMetadata<Runtime> {
using StoredPointer = typename Runtime::StoredPointer;
TargetValueMetadata(MetadataKind Kind,
const TargetTypeContextDescriptor<Runtime> *description)
: TargetMetadata<Runtime>(Kind), Description(description) {}
/// An out-of-line description of the type.
TargetSignedPointer<Runtime, const TargetValueTypeDescriptor<Runtime> * __ptrauth_swift_type_descriptor> Description;
...
};
我们看到初始化方法中有Kind
和description
两个属性,但是底下只看到一个属性description
,那Kind
应该还在父类中,我们继续向上探索父类TargetMetadata
/// Bounds for metadata objects.
struct TargetMetadata {
using StoredPointer = typename Runtime::StoredPointer;
/// The basic header type.
typedef TargetTypeMetadataHeader<Runtime> HeaderType;
constexpr TargetMetadata()
: Kind(static_cast<StoredPointer>(MetadataKind::Class)) {}
constexpr TargetMetadata(MetadataKind Kind)
: Kind(static_cast<StoredPointer>(Kind)) {}
#if SWIFT_OBJC_INTEROP
protected:
constexpr TargetMetadata(TargetAnyClassMetadata<Runtime> *isa)
: Kind(reinterpret_cast<StoredPointer>(isa)) {}
#endif
private:
/// The kind. Only valid for non-class metadata; getKind() must be used to get
/// the kind value.
StoredPointer Kind;
public:
/// Get the metadata kind.
MetadataKind getKind() const {
return getEnumeratedMetadataKind(Kind);
}
/// Set the metadata kind.
void setKind(MetadataKind kind) {
Kind = static_cast<StoredPointer>(kind);
}
...
};
果不其然,我们找到了Kind
,不过是StoredPointer
类型的,他是Runtime::StoredPointer
的别名,那Runtime
传进来的是模版,前面我们看到模版传进来的的是InProcess
,在InProcess
中,我们看到using StoredPointer = uintptr_t;
,我们在点开uintptr_t
,看到typedef unsigned long uintptr_t;
,那本质上Kind
就是unsigned long
类型。其实我们从上面的代码中就可以看出,这个Kind
在与OC的类交互时,传进来的是isa
,不然就是MetadataKind
。
用swift代码简单模拟StructMetadata
上面的代码翻看下来,整个StructMetadata
就只有Kind
和Description
2个属性,Kind
我们知道了是unsigned long
类型,Description
暂时还不知道是啥,不过没关系,从名称上来看是一个指针,我们先用一个UnsafeRawPointer
来替代他(后面在详细解析他),这样我们可以写如下代码
struct Teacher {
var age = 18
var name = "kody"
}
struct StructMetadata {
var kind: Int
var Description: UnsafeRawPointer
}
// 通过源码我们可以知道Type类型对应的就是Metadata,这里记住要转成Any.Type,不然typesize不一致,不让转
let ptr = unsafeBitCast(Teacher.self as Any.Type, to: UnsafeMutablePointer<StructMetadata>.self)
print("0x\(String(ptr.pointee.kind, radix: 16))") //0x200
非常棒,输出0x200
,和表格里的一致,换成枚举也能得到对应的值,如果强转Teacher?.self
,那会得到0x202
,这样说明我们的思路可行的。
Swift
基础类型底层的详细探索
在上面搜索StructMetadata
的时候,细心的小伙伴可以发现下面的代码:
template <typename Runtime> struct TargetGenericMetadataInstantiationCache;
template <typename Runtime> struct TargetAnyClassMetadata;
template <typename Runtime> struct TargetClassMetadata;
template <typename Runtime> struct TargetStructMetadata;
template <typename Runtime> struct TargetOpaqueMetadata;
template <typename Runtime> struct TargetValueMetadata;
template <typename Runtime> struct TargetForeignClassMetadata;
template <typename Runtime> struct TargetContextDescriptor;
template <typename Runtime> class TargetTypeContextDescriptor;
template <typename Runtime> class TargetClassDescriptor;
template <typename Runtime> class TargetValueTypeDescriptor;
template <typename Runtime> class TargetEnumDescriptor;
template <typename Runtime> class TargetStructDescriptor;
template <typename Runtime> struct TargetGenericMetadataPattern;
这里定义了大部分我们想要的底层Metadata
了,所以我们只要分析这些类或者结构体,就能得到基础类型的底层结构了。
一开始是想写在一起的,但是感觉太长了,所以会分文章写
待续...