swift 类的结构2

90 阅读2分钟

「这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战」。

1.2 swift_allocObject 源码分析
void *swift::swift_slowAlloc(size_t size, size_t alignMask) {
  void *p;
  //这个检查也强制“默认”对齐使用AlignedAlloc。  if (alignMask <= MALLOC_ALIGN_MASK) {
#if defined(__APPLE__)
    p = malloc_zone_malloc(DEFAULT_ZONE(), size);
#else
    p = malloc(size);// 堆中创建size大小的内存空间,用于存储实例变量
#endif
  } else {
    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;
}
  • 进入swift_slowAlloc函数,其内部主要是通过malloc_zone_malloc在堆中分配size大小的内存空间,并返回内存地址,主要是用于存储实例变量
1.3 查看HeapObject 并 计算类的大小
// The members of the HeapObject header that are not shared by a
// standard Objective-C instance
#define SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS       \
  InlineRefCounts refCounts ///引用计数

/// The Swift heap-object header.
/// This must match RefCountedStructTy in IRGen.
struct HeapObject {
  /// This is always a valid pointer to a metadata object.
  HeapMetadata const *metadata;/// 元数据

  SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS; ///引用计数
  • metadata:是 HeapMetedata类型
  • refCounts : 引用计数

【总结】

  • Swift中实例对象,默认的比OC中多了一个refCounted引用计数大小,默认属性占16字节 : metadata(struct)8字节和refCounts(class)8字节
  • OC中实例对象的本质是结构体,是以objc_object为模板继承的,其中有一个isa指针,占8字节
1.4【验证+拓展】
//验证
class HJPerson {
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        print(class_getInstanceSize(HJPerson.self))
    }
}
打印 : 16

//拓展
class HJPerson {
    var age : Int = 20
    var name : String = "HJ"
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        print(class_getInstanceSize(HJPerson.self))
    }
}
打印 : 40

验证的确一个空类的大小为16;那么为啥第二个是40呢?
我们通过打印Int 和 String 内存大小来验证

//********* Int底层定义 *********
@frozen public struct Int : FixedWidthInteger, SignedInteger {...}

//String底层定义
@frozen public struct String {...}

//验证 
print(MemoryLayout<Int>.stride)
print(MemoryLayout<String>.stride)

打印
8
16
  • 从打印的结果中可以看出,Int类型占8字节,String类型占16字节,这点与OC中是有所区别的 ,以后再进行详细讲解吧。
  • 所以这也解释了为什么HJPerson的内存大小等于40,即40 = metadata(8字节) +refCount(8字节)+ Int(8字节)+ String(16字节)

二、Swift中类的结构探索

  • 在OC中类是从objc_class模板继承过来的

  • 在Swift中,类的结构在底层是HeapObject,其中有metadata +refCounts

2.1 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) {}

};