初探Swift 5下Any的实现逻辑

863 阅读2分钟

结构

相关的结构体

以下结构体是Any的实现会涉及到的,大部分都是Swift的源码中简化过来的

struct Any {   // 没有找到源码中的声明,主要从编译产物中翻译过来
   int8_t data[24];   // 用于存储对象数据
   Metadata *meta;    // 记录对象的类型
}

struct HeapObject {   // 类似于Objective-C的实例对象
    Metadata* metadata;
    // uint_t refCounts;  // swift对象的引用计数等
}

struct Metadata {   // 类似于Objective-C的类
    int64_t kind;
    // 不通类型会有不同的数据段,此次省略
}

struct ValueWitnessTable {
    uintptr_t initializeBufferWithCopyOfBuffer;
    uintptr_t destroy;
    uintptr_t initializeWithCopy;
    uintptr_t assignWithCopy;
    uintptr_t initializeWithTake;
    uintptr_t assignWithTake;
    uintptr_t getEnumTagSinglePayload;
    uintptr_t storeEnumTagSinglePayload;
    int_t size;
    int_t stride;
    uint32_t flags;
    uint32_t extraInhabitantCount;
    uint_t getEnumTag;
    uintptr_t destructiveProjectEnumData;
    uintptr_t destructiveInjectEnumTag;
}

内存布局

Any对象是由24个字节和一个Metadata的指针组成。简化后如下显示

image.png

存储

Any对象的24个字节,可能存储如下两种数据:

  • 直接存储对象数据,如大小较小的Struct对象
  • 使用8个字节(64位),存储一个HeapObject的指针(Swift的引用对象)

赋值

针对不同类型的数据,Any对象的赋值有如下几种:

Struct / Enum

  • 小于等于 24字节
  1. 存储目标的Metadata对象
  2. 直接对象数据
  • 大于 24字节
  1. 存储目标的Metadata对象
  2. 调用swift_allocObject创建HeapObject对象
  3. 存储HeapObject的指针

Class

  1. 存储目标的Metadata对象
  2. 调用swift_retain增加引用计数
  3. 存储HeapObject的指针

Any

赋值Any对象时,通过copy函数$sypWOc(编译器生成的Any对象的Copy函数)。

$sypWOc主要执行以下操作:

  1. 复制Metadata对象
  2. 读取ValueWitnessTable对象(Any -> Metadata -> ValueWitnessTable)
  3. 通过ValueWitnessTable对象的initializeBufferWithCopyOfBuffer函数,复制数据

image.png

销毁

编译器会生成Any对象的destroy函数(__swift_destroy_boxed_opaque_existential_0),主要执行以下操作:

  1. 读取ValueWitnessTable对象(Any -> Metadata -> ValueWitnessTable)
  2. 通过ValueWitnessTable对象的flags,做位判断(IsNonInline = 0x0002000)
  3. 如果是inline(即结果为0),调用ValueWitnessTable对象的destroy进行销毁;否则,调用swift_release,减少引用计数