Swift类方针、实例方针底层那些事

背景

iOS类方针、实例方针实质咱们应该都很清楚,但是swift类、方针底层又是怎么样的呢?,这篇文章咱们一同研讨下。

方针

  • 理解实例方针的创立过程以及内存布局
  • 理解类方针的内部布局以及在oc和swift上的差异

探求

1. 探求方针

初始化过程

示例代码

class ClassAndInstence {
    var age: Int = 18
    var name: String = "小李"
}
func main() {
    //类和方针
    let classAndIntence = ClassAndInstence()
}
main()

在创立classAndIntence方针的那一行下一个断点

Swift类方针、实例方针底层那些事

运转…

Swift类方针、实例方针底层那些事
能够发现创立ClassAndInstence示例调用了__allocating_init函数

由于__allocating_init在swift源码里是看不到的,所以咱们需求再往下看看有什么依据可寻

swift源码编译能够参考我的这篇文章

咱们能够在xcode下一个符号断点进入__allocating_init的内部完成

Swift类方针、实例方针底层那些事

再次运转,__allocating_init内部逻辑如下

Swift类方针、实例方针底层那些事

  • 第一步:执行swift_allocObject创立方针
  • 第二步:执行ClassAndInstence的init办法

swift_allocObject办法在swift源码中的界说如下:

Swift类方针、实例方针底层那些事

字段 意义
metadata 包名.类名
requiredSize 需求大小
requiredAlignmentMask requiredAlignmentMask

这儿的CALL_IMPL的界说主要是用于分发事情,后续能够看到swift源码里的办法调用根本都是经过它进行的

Swift类方针、实例方针底层那些事

断点持续走就走到CALL_IMPL派发的swift_allocObject办法里了

Swift类方针、实例方针底层那些事

  1. 第一步:经过swift_slowAlloc分配内存,分配给
// Apple malloc is always 16-byte aligned. 
#define MALLOC_ALIGN_MASK 15
// When alignMask == ~(size_t(0)), allocation uses the "default"
// _swift_MinAllocationAlignment. This is different than calling swift_slowAlloc
// with `alignMask == _swift_MinAllocationAlignment - 1` because it forces
// the use of AlignedAlloc. This allows manually allocated to memory to always
// be deallocated with AlignedFree without knowledge of its original allocation
// alignment.
//
// For alignMask > (_minAllocationAlignment-1)
// i.e. alignment == 0 || alignment > _minAllocationAlignment:
//   The runtime must use AlignedAlloc, and the standard library must
//   deallocate using an alignment that meets the same condition.
//
// For alignMask <= (_minAllocationAlignment-1)
// i.e. 0 < alignment <= _minAllocationAlignment:
//   The runtime may use either malloc or AlignedAlloc, and the standard library
//   must deallocate using an identical alignment.
//堆中拓荒空间(空间大小,内存对齐大小)
void *swift::swift_slowAlloc(size_t size, size_t alignMask) {
  void *p;
  // This check also forces "default" alignment to use AlignedAlloc.
  // 内存对其大小 <= 15 :MAlloc运用默认的内存空间拓荒空间,也是16字节对齐
  if (alignMask <= MALLOC_ALIGN_MASK) {
#if defined(__APPLE__) && SWIFT_STDLIB_HAS_DARWIN_LIBMALLOC
    p = malloc_zone_malloc(DEFAULT_ZONE(), size);
#else
    p = malloc(size);
#endif
  } else { // 内存对齐大小 > 15 : MAlloc运用传入的内存大小进行拓荒,也是外部传入的字节对齐
    size_t alignment = (alignMask == ~(size_t(0)))
                           ? _swift_MinAllocationAlignment
                           : alignMask + 1;
    p = AlignedAlloc(size, alignment);
  }
  if (!p) swift::crash("Could not allocate memory.");
  return p;
}
  • 经过判别内存对齐掩码是否大于15 判别不同的拓荒内存方式,mac工程传入内存对齐掩码是7,经过以下方式创立内存
    Swift类方针、实例方针底层那些事
    假如大于16字节位数,那么运用AlignedAlloc办法
    Swift类方针、实例方针底层那些事
  1. 第二步:经过HeapObject办法结构一个HeapObject方针,而且绑定到object上

    Swift类方针、实例方针底层那些事

  2. 第三步:最终回来一个HeapObject类型的方针

到这儿咱们知道,swift的类示例方针在底层都是创立一个heapObject类型的方针,咱们需求深入研讨下heapObject…

HeapObject界说如下:

Swift类方针、实例方针底层那些事

成员 类型 意义
metadata HeapMetadata 指向元类的指针 占8个字节
refCounts InlineRefCounts 引用计数相关 占8个字节
HeapObject带参数结构
HeapObject不带参数结构

看代码是经过HeapObject结构函数调用refCounts初始化引用计数

refCounts看如下调用栈是调用到了RefCountBitsT类的结构函数设置

Swift类方针、实例方针底层那些事
Swift类方针、实例方针底层那些事
后续研讨内存管理睬要点介绍这儿

总结:

  • 方针在底层都是HeapObject类型
  • HeapObject包括信息:
    • metadata指向类方针信息信息
    • refCounts保存引用计数相关

一个方针内存分局


class ClassAndInstence {
    var age: Int = 18
    var name: String = "葛高召"
}
let classAndIntence = ClassAndInstence()
print(MemoryLayout<Int>.stride)
print(MemoryLayout<String>.stride)
print(class_getInstanceSize(ClassAndInstence.self))
  • metadata占8个字节
  • refCounts占8个字节
  • 再加上age的8个字节
  • name占16个字节
  • 所以总共是40个字节

2. 探求类方针

方针在底层中的结构是HeapObject结构体,其第一个特点为metadata,因此从这个特点出发来检查类的结构

查找HeapMetadata

template <typename Target> struct TargetHeapMetadata;
using HeapMetadata = TargetHeapMetadata<InProcess>;
  • 上文可知方针结构体HeapObject包括有HeapMetadata结构体,方针经过它来查找对应的类信息
  • 点击进入HeapMetadata的界说,发现它是TargetHeapMetaData类型的别号
  • 而且接收了一个参数Inprocess

在HeapObject结构时对HeapMetadata进行界说,传入TargetHeapMetaData的参数Inprocess

Swift类方针、实例方针底层那些事

TargetHeapMetaData

template <typename Runtime>
struct TargetHeapMetadata : TargetMetadata<Runtime> {
  using HeaderType = TargetHeapMetadataHeader<Runtime>;
  TargetHeapMetadata() = default;
  constexpr TargetHeapMetadata(MetadataKind kind)
    : TargetMetadata<Runtime>(kind) {}
  constexpr TargetHeapMetadata(TargetAnyClassMetadataObjCInterop<Runtime> *isa)
    : TargetMetadata<Runtime>(isa) {}
};
using HeapMetadata = TargetHeapMetadata<InProcess>;

阐明:

  • TargetHeapMetaData其实质是一个模板类型,其中界说了一些所需的数据结构
  • 这个结构体中没有特点,只有初始化办法
  • 初始化办法中传入了一个MetadataKind类型的参数,之后就能够回来TargetMetaData方针
  • 同时能够看到这儿传入的kind也便是上面的inprocess了
  • 最终把传入kind传入基础结构体TargetMetadata的初始化办法

TargetMetaData

Swift类方针、实例方针底层那些事

  • 在TargetMetaData中能够看到有一个Kind特点,这是在构建方针时传入的那个参数

检查MetadataKind

/// Kinds of Swift metadata records.  Some of these are types, some
/// aren't.
enum class MetadataKind : uint32_t {
#define METADATAKIND(name, value) name = value,
#define ABSTRACTMETADATAKIND(name, start, end)                                 
  name##_Start = start, name##_End = end,
#include "MetadataKind.def" //包括所有类型的界说
  /// The largest possible non-isa-pointer metadata kind value.
  ///
  /// This is included in the enumeration to prevent against attempts to
  /// exhaustively match metadata kinds. Future Swift runtimes or compilers
  /// may introduce new metadata kinds, so for forward compatibility, the
  /// runtime must tolerate metadata with unknown kinds.
  /// This specific value is not mapped to a valid metadata kind at this time,
  /// however.
  LastEnumerated = 0x7FF,
};
  • 能够看到它是uint32_t类型
  • MetadataKind文件界说所有类型
    Swift类方针、实例方针底层那些事

检查MetadataKind

TargetMetaData结构体界说中有一个办法getClassObject,它就能够用来获取类方针

template <>
  inline const ClassMetadata *Metadata::getClassObject() const {
    switch (getKind()) {
    case MetadataKind::Class: {//假如kind是class
      // Native Swift class metadata is also the class object.
      //将当时指针强转为ClassMetadata类型
      return static_cast<const ClassMetadata *>(this);
    }
#if SWIFT_OBJC_INTEROP
    case MetadataKind::ObjCClassWrapper: {
      // Objective-C class objects are referenced by their Swift metadata wrapper.
      auto wrapper = static_cast<const ObjCClassWrapperMetadata *>(this);
      return wrapper->Class;
    }
#endif
    // Other kinds of types don't have class objects.
    default:
      return nullptr;
    }
  }
  • 在办法中的核心逻辑是经过kind来判别当时是哪种类型
  • 里咱们需求的是类类型,因此判别为MetadataKind::Class,就会回来ClassMetadata类型

验证:

Swift类方针、实例方针底层那些事

命令:

  • po metadata->getKind()
    • 得到其kind是Class
    • po metadata->getClassObject() + x/8g 0x0000000110efdc70
    • 这个地址中存储的是元数据信息!

阐明:

  • 传递进来的Kind发现能够判别为类
  • 经过办法调用最终得到的是一个类方针,也便是类
  • 经过x/8g检查类信息,里面便是存储的元数据信息

留意:

  • TargetMetadata 和 TargetClassMetadata 实质上是相同的
  • 由于在内存结构中,能够直接进行指针的转化,所以能够说,咱们认为的结构体,其实便是TargetClassMetadata

TargetClassMetadata

template<typenameRuntime>
structTargetClassMetadata:publicTargetAnyClassMetadata<Runtime>{
...
//swift特有的标志
ClassFlagsFlags;
//实力方针内存大小
uint32_tInstanceSize;
//实例方针内存对齐方式
uint16_tInstanceAlignMask;
//运转时保留字段
uint16_tReserved;
//类的内存大小
uint32_tClassSize;
//类的内存首地址
uint32_tClassAddressPoint;
...
}

阐明:

  • 假如运转时支持Objective-C互操作性,这个类就会承继TargetAnyClassMetadataObjCInterop,不然它承继自TargetAnyClassMetadata。
  • 包括了许多特点,这些都归于类结构信息

TargetAnyClassMetadataObjCInterop

struct TargetAnyClassMetadataObjCInterop
    : public TargetAnyClassMetadata<Runtime> {
    constexpr TargetAnyClassMetadataObjCInterop(
      TargetAnyClassMetadataObjCInterop<Runtime> *isa, //isa信息
      TargetClassMetadataObjCInterop *superclass) //superclass信息
      : TargetAnyClassMetadata<Runtime>(isa, superclass),
        CacheData{nullptr, nullptr}, //cache信息
        Data(SWIFT_CLASS_IS_SWIFT_MASK) {} //data信息
...
}
  • 假如支持oc则TargetClassMetadata承继TargetAnyClassMetadataObjCInterop
  • 类中也会存在isa、superclass、cache、data,和OC的底层类结构彻底相同

而TargetClassMetadata承继TargetAnyClassMetadata再看下TargetAnyClassMetadata

TargetAnyClassMetadata

/// The portion of a class metadata object that is compatible with
/// all classes, even non-Swift ones.
template <typename Runtime>
struct TargetAnyClassMetadata : public TargetHeapMetadata<Runtime> {
  using StoredPointer = typename Runtime::StoredPointer;
  using StoredSize = typename Runtime::StoredSize;
  using TargetClassMetadata = TargetClassMetadataType<Runtime>;
protected:
  constexpr TargetAnyClassMetadata(
      //isa信息
      TargetAnyClassMetadataObjCInterop<Runtime> *isa,
      //superclass信息
      TargetClassMetadata *superclass)
      : TargetHeapMetadata<Runtime>(isa), Superclass(superclass) {}
public:
  constexpr TargetAnyClassMetadata(TargetClassMetadata *superclass)
      : TargetHeapMetadata<Runtime>(MetadataKind::Class),
        Superclass(superclass) {}
  // Note that ObjC classes do not have a metadata header.
  /// The metadata for the superclass.  This is null for the root class.
  TargetSignedPointer<Runtime, const TargetClassMetadata *
                                   __ptrauth_swift_objc_superclass>
      Superclass;
  /// Is this object a valid swift type metadata?  That is, can it be
  /// safely downcast to ClassMetadata?
  bool isTypeMetadata() const {
    return true;
  }
  /// A different perspective on the same bit.
  bool isPureObjC() const {
    return !isTypeMetadata();
  }
};
  • TargetAnyClassMetadata是所有的类结构,不单单是给Swift用的
  • 承继自TargetHeapMetadata,这也证明类本身也是方针

总结:

  • 类方针在底层都是归于TargetAnyClassMetadata
    • oc时底层最基础类型是TargetAnyClassMetadataObjCInterop,用有和oc相同的成员
    • swift时底层最基础类型是TargetAnyClassMetadata

到这儿就完毕,感谢您的阅读,欢迎阅读我的其他文章