初探Swift底层Metadata

前言

本文将会初次探索Swift底层,但随着Swift版本更新,底层结构可能会变更(ABI已经稳定,即便调整,应该也是微调),因此在这边记录下版本号。c++

Swift源码版本是5.3.1Xcode版本是12.3(其实对应的源码版本是5.3.2,不太小版本,懒的更新了)swift

本文会初步探索Metadata,详细的底层结构会在文章末尾附上连接。markdown

因为比较深刻底层,会有较多指针类型,若是你不是很熟悉Swift类型的指针,能够先看下我上一篇写的文章闭包

经过Mirror获取类型的Metadata

虽然Swift重心在强调静态类型上,但它经过反射MirrorAPI,容许代码在运行时检查和操做任意值。这边咱们经过Mirror的源码,看如何获取数据类型的元数据Metadata的,咱们先以struct为突入口。app

咱们在源码中运行以下代码post

struct Teacher {var age = 18; var name = "kody"}

let t = Teacher.init()

let mirror = Mirror(reflecting: t)
复制代码

在初始化mirror前,咱们在init(reflecting subject: Any)方法中打上断点,随着断点走: ui

咱们能够看到这些框起来的方法(获取类型,及属性个数)都调用了一个call,而后拿到了一个impl变量,而后返回impl的某个属性就能够了,因此这边call方法和impl是什么比较关键,咱们继续往下走:this

先进入call方法(部分截图,太长了): spa

初步看到implReflectionMirrorImpl,这里是一个相似闭包的东西,参数传了一个ReflectionMirrorImpl类型,接着往下走,看看谁会调用: 指针

咱们发现经过调用type->getKind()获取数据类型,发现是一个结构体,而后进入switch的结构体的分支,发现impl变成了StructImpl,咱们搜索StructImpl能够看到

从图中咱们能够发现:StructImplReflectionMirrorImpl的子类,Struct的元数据MetadataStructMetadata

简单的整理下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;

  ...
};
复制代码

咱们看到初始化方法中有Kinddescription两个属性,可是底下只看到一个属性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就只有KindDescription2个属性,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了,因此咱们只要分析这些类或者结构体,就能获得基础类型的底层结构了。

一开始是想写在一块儿的,可是感受太长了,因此会分文章写

待续...

相关文章
相关标签/搜索