swift探索1:类、对象

为了探索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拿到PersonMetadata赋值给%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种类总结如下

namevalue
Class0x0
Struct0x200
Enum0x201
Optional0x202
ForeignClass0x203
Opaque0x300
Tuple0x301
Function0x302
Existential0x303
Metatype0x304
ObjCClassWrapper0x305
ExistentialMetatype0x306
HeapLocalVariable0x400
HeapGenericLocalVariable0x500
ErrorObject0x501
LastEnumerated0x7FF
  • 回到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;
    ...
}

总结

综上所述,当metadatakind为Class时,有如下继承链:

图片

源码分析-5

  • 当前类返回的实际类型是 TargetClassMetadata,而TargetMetaData中只有一个属性kindTargetAnyClassMetaData中有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_tmethodList

    • swift中的方法存储在 metadata 元数据中

  • 引用计数

    • OC中的ARC维护的是散列表

    • Swift中的ARC是对象内部有一个refCounts属性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值