初探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了,因此咱們只要分析這些類或者結構體,就能獲得基礎類型的底層結構了。

一開始是想寫在一塊兒的,可是感受太長了,因此會分文章寫

待續...

相關文章
相關標籤/搜索