Swift类和对象的底层探索分析

目录
  • 引言
  • 1. 对象
    • 1.1 上层代码中查找
      • 1.1.1 查找对象调用方法
      • 1.1.2 设置符号断点
    • 1.2 swift_allocObject
    • 1.3 swift_showAlloc
    • 1.4 查看HeapObject结构体
    • 1.5 对象内存大小计算
    • 1.6 总结
  • 2. 类
    • 2.1 查找HeapMetadata
    • 2.2. TargetHeapMetaData
    • 2.3. TargetMetaData
    • 2.4. TargetClassMetadata
    • 2.5. TargetAnyClassMetadata

引言

在上文已经了解了SIL,接下来主要通过Swift源码和SIL剖析底层。本文主要通过底层源码探索类和对象在底层的结构

主要内容:

  • 对象

1. 对象

通过源码中探索Swift对象创建过程以及最终得到的对象结构。

1.1 上层代码中查找

通过符号断点调试来查找底层调用方法

源码:

class WYStudent {
    var age: Int = 18
    var name: String = "WY"
}
var stu = WYStudent();

1.1.1 查找对象调用方法

通过断点查看发现是通过__allocating_init()方法实现对象的创建

添加断点

查看调用方法

1.1.2 设置符号断点

符号断点:

查看:

说明:

  • 在上面SIL的认识中已经知道了对象是通过__allocating_init()来创建的,在此处打断点查看
  • 在__allocating_init()方法中可以看到会调用swift_allocObject()方法
  • 因此接下来就需要在源码中查看该方法
  • __allocating_init()方法中做了两件事
    • 调用swift_allocObject创建对象
    • 调用init()初始化对象,这个init方法是类默认提供的,也是默认调用的

1.2 swift_allocObject

说明:

  • 通过swift_slowAlloc分配内存,并进行内存字节对齐,传入开辟的内存空间大小和对齐位数
  • 通过HeapObject方法构造一个HeapObject对象,并且绑定到object上
  • 因此此时的object就是一个heapObject对象
  • 函数的返回值是HeapObject类型,所以当前对象的内存结构就是HeapObject的内存结构

1.3 swift_showAlloc

// Apple malloc is always 16-byte aligned.
#  define MALLOC_ALIGN_MASK 15

说明:

  • 通过swift_slowAlloc用来分配内存空间
  • 这里会通过对齐位数来判断使用哪种方法来分配空间
  • 最小的对齐位数是16字节,如果传入的位数小于16字节,那么就是用16字节对齐,也就是使用malloc方法
  • 如果大于16字节位数,那么使用AlignedAlloc方法

1.4 查看HeapObject结构体

结构体

refCounts查看:

typedef RefCounts<InlineRefCountBits> InlineRefCounts;
//是一个类,所以它的对象就是8个字节
class RefCounts {
  std::atomic<RefCountBits> refCounts;//引用计数
  ...
}

说明:

  • 结构体内包含一个成员,metadata
  • HeapObject()初始化器,会初始化metadata和refCounts,因此对象中会有这两种属性
  • 其中metadata类型是HeapMetadata,是一个指针类型,占8字节,其实它就是类信息
  • refCounts是引用计数,也占有8个字节
  • refCounts的类型是InlineRefCounts
  • 而InlineRefCounts是一个类RefCounts的别名
  • RefCounts是一个类,所以refCounts占8个字节

1.5 对象内存大小计算

说明:

  • metadata占8个字节
  • refCounts占8个字节
  • 再加上age的8个字节
  • name占8个字节
  • 所以总共是40个字节

1.6 总结

实例对象的底层结构是HeapObject结构体

默认16字节内存大小,metadata 8字节 + refCounts 8字节

metadata是类信息结构,下面会分析

refCounts是引用计数,后面也会详细分析

Swift中对象的内存分配流程是:

__ allocating_init --> swift_allocObject_ --> _swift_allocObject --> swift_slowAlloc --> malloc

2. 类

对象在底层中的结构是HeapObject结构体,其第一个属性为metadata,因此从这个属性出发来查看类的结构

2.1 查找HeapMetadata

代码:

using HeapMetadata = TargetHeapMetaData<Inprocess>;

说明:

  • 上文可知对象结构体HeapObject包含有HeapMetadata结构体,对象通过它来查找对应的类信息
  • 点击进入HeapMetadata的定义,发现它是TargetHeapMetaData类型的别名
  • 并且接收了一个参数Inprocess

2.2. TargetHeapMetaData

代码:

//模板类型
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
};

说明:

  • TargetHeapMetaData其本质是一个模板类型,其中定义了一些所需的数据结构
  • 这个结构体中没有属性,只有初始化方法
  • 初始化方法中传入了一个MetadataKind类型的参数,之后就可以返回TargetMetaData对象
  • 同时可以看到这里传入的kind也就是上面的inprocess了
  • 该初始化方法构造的对象需要通过该参数来确定

2.3. TargetMetaData

代码:

说明:

  • 在TargetMetaData中可以看到有一个Kind属性,这是在构建对象时传入的那个参数

查看MetadataKind

说明:

  • 可以看到它是uint32_t类型

类型

说明:

  • 进入MetadataKind定义,里面有一个#include "MetadataKind.def"
  • 点击进入,其中记录了所有类型的元数据

getClassObject方法:

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;
    }
  }

说明:

  • 在TargetMetaData结构体定义中有一个方法getClassObject,它就可以用来获取类对象,也就是类
  • 在方法中的核心逻辑是通过kind来判断当前是哪种类型,之后返回
  • 这里我们需要的是类类型,因此判断为MetadataKind::Class,就会返回ClassMetadata类型

验证:

命令:

po metadata->getKind()

得到其kind是Class

po metadata->getClassObject() + x/8g 0x0000000110efdc70

这个地址中存储的是元数据信息!

说明:

  • 传递进来的Kind发现可以判断为类
  • 通过方法调用最后得到的是一个类对象,也就是类
  • 通过x/8g查看类信息,里面就是存储的元数据信息

注意:

  • TargetMetadata 和 TargetClassMetadata 本质上是一样的
  • 因为在内存结构中,可以直接进行指针的转换,所以可以说,我们认为的结构体,其实就是TargetClassMetadata

2.4. TargetClassMetadata

代码:

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

2.5. TargetAnyClassMetadata

代码:

说明:

  • TargetAnyClassMetadata是所有的类结构,不单单是给Swift用的
  • 继承自TargetHeapMetadata,这也证明类本身也是对象
  • 提供有isa、superclass、cache、data,和OC的底层类结构完全一样

以上就是Swift类和对象的底层探索分析的详细内容,更多关于Swift类和对象的资料请关注我们其它相关文章!

(0)

相关推荐

  • Swift 字符串类型及常用方法详解总结

    目录 1. 构造 2. 拼接 3. 字符 4. 转义符 5. 常用方法 Swift 字符串类型及常用方法 1. 构造 // 直接赋值 text = "" // 1. 构造方法 text = String() // "" // 字符串构造 text = String("William") // "William" // 整型构造 text = String(888) // "888" // 浮点型构造 tex

  • Swift 5.1 之类型转换与模式匹配的教程详解

    类型转换在Swift中使用 is 和 as 操作符实现. 类型检查 使用操作符 is 检查一个实例是否是某个确定的类以及其继承体系的父类或子类类型.如果是某个确定的类(该类继承体系的父类或子类)类型,则返回 true ,否则返回 false . class Cat { func hairColor() -> String { return "五颜六色" } } class WhiteCat: Cat { override func hairColor() -> String

  • Swift如何使用类型擦除及自定义详解

    前言 在 Swift 的世界中,如果我们将协议称之为国王,那么泛型则可以视作皇后,所谓一山不容二虎,当我们把这两者结合起来使用的时候,似乎会遇到极大的困难.那么是否有一种方法,能够将这两个概念结合在一起,以便让它们成为我们前进道路上的垫脚石,而不是碍手碍脚的呢?答案是有的,这里我们将会使用到类型擦除 (Type Erasure) 这个强大的特性. 你也许曾听过类型擦除,甚至也使用过标准库提供的类型擦除类型如 AnySequence.但到底什么是类型擦除? 如何自定义类型擦除? 在这篇文章中,我将

  • 解析Swift语言面相对象编程中的继承特性

    取大于形态的能力被定义为继承.一般一个类可以从另一个类继承属性和方法.类可以进一步划分到子类和超类. 子类:当一个类从另一个类继承属性,方法和功能被称为子类 超类:类包含属性,方法和功能被其它类继承称为超类 Swift 中类包含父类和调用访问方法,属性,功能和重写方法.另外,属性观察者也用于添加属性和修改所存储的或计算的特性的方法. 基类 一个类如果不从其它类继承方法,属性或功能,那么它被称为"基类". 复制代码 代码如下: classStudDetails{var stname:St

  • 详解Swift面向对象编程中的方法(method)

    struct Point { var x:Double var y:Double mutating func move(x:Double,y:Double) { self = Point(x: self.x+x,y: self.y+y) } static func name(){ print("Point") } } Point.name() 一.引言 方法只是一个术语,其实就是将函数与特定的类型结合,类.结构体.枚举都可以定义方法,方法又分为实例方法和类型方法,类型方法类似于Obje

  • Swift类和对象的底层探索分析

    目录 引言 1. 对象 1.1 上层代码中查找 1.1.1 查找对象调用方法 1.1.2 设置符号断点 1.2 swift_allocObject 1.3 swift_showAlloc 1.4 查看HeapObject结构体 1.5 对象内存大小计算 1.6 总结 2. 类 2.1 查找HeapMetadata 2.2. TargetHeapMetaData 2.3. TargetMetaData 2.4. TargetClassMetadata 2.5. TargetAnyClassMeta

  • java类和对象原理与用法分析

    本文实例讲述了java类和对象原理与用法.分享给大家供大家参考,具体如下: 面向对象编程OOP 类:相似对象的集合. 对象 对象:实体.一切可以被描述的事物. 属性:特征. 方法:动作,行为. 类和对象的区别 [1]类时抽象的,对象是具体的. [2]类是一个模板,创建出来的对象具备共同的属性和方法. [3]类是一种数据烈性.引用数据类型. 语法 public classs 类名{ //定义属性部分 属性1的类型 属性1: 属性2的类型 属性2: ... 属性3的类型 属性n; //定义方法部分

  • JavaScript类数组对象转换为数组对象的方法实例分析

    本文实例分析了JavaScript类数组对象转换为数组对象的方法.分享给大家供大家参考,具体如下: 1.类数组对象: 拥有length属性,可以通过下标访问: 不具有数组所具有的方法. 2.为什么要将类数组对象转换为数组对象? 数组对象Array有很多方法:shift.unshift.splice.slice.concat.reverse.sort,ES6又新增了一些方法:forEach.isArray.indexOf.lastIndexOf.every.some.map.filter.redu

  • 深入浅出分析Java 类和对象

    目录 一.什么是类 二.Java的类和C语言的结构体异同 三.类和类的实例化 类的声明 实例化的对象,成员遵循默认值规则 类的实例化 静态属性(静态成员变量) 四.构造方法 创建构造方法 this 一.什么是类 类(Class)是面向对象程序设计(OOP,Object-Oriented Programming)实现信息封装的基础.类是一种用户自定义的引用数据类型,也称类类型.每个类包含数据说明和一组操作数据或传递消息的函数,类的实例称为对象 类的实质是一种引用数据类型,类似于 byte,shor

  • C++深入探索类和对象之封装及class与struct的区别

    目录 封装的意义 访问权限 class和struct的区别 成员属性私有 案例练习 封装的意义 封装是C++三大面向对象之一 意义: 1.设计类的时候,属性和行为写在一起,表现事物 2.类在设计时,可以把属性和行为放在不同的权限下,加以控制 语法:class 类名 {访问权限:属性 / 行为}: 示例1:设计一个直角三角形,并求解他的面积 #include<iostream> using namespace std; //class代表设计一个类,后面跟着的是类名 class taiAngle

  • C++类和对象深入探索之分文件编写点和圆的关系详解

    目录 创建圆心类 创建圆类 判断点圆关系函数 最终实现 总结 上一篇封装直达 创建圆心类 point.h #pragma once #include<iostream> using namespace std; //创建圆心类 class Point { public: void setM_x(int x); int getM_x(); void setM_y(int y); int getM_y(); private: int m_x; int m_y; }; 把圆心的横纵坐标设为私有,公共

  • C++分析类的对象作类成员调用构造与析构函数及静态成员

    目录 类对象作为成员 静态成员 定义和分类 静态成员变量 静态成员函数 总结 类对象作为成员 C++类中的成员可以是另一个类的对象,我们称该成员为 对象成员 例如: class Phone {} class Person { Phone p: } tips:当类中成员是其他类对象时,我们称该成员为 对象成员 Person类中有对象p作为成员,Phone为对象成员,那么当创建Person对象时,Phone与Person的构造和析构的顺序是谁先谁后? 那让我们在两个类中加上一些输出语句做提示就好了,

  • Python类和对象的定义与实际应用案例分析

    本文实例讲述了Python类和对象的定义与实际应用.分享给大家供大家参考,具体如下: 1.DVD管理系统 # -*- coding:utf-8 -*- #! python3 class dvd: def __init__(self,name,price,state): self.name=name; self.price=price self.state=state def __str__(self): stat='已借出'; if self.state==1: stat='未借出' retur

  • java各种类型对象占用内存情况分析

    前言 其实一般的程序猿根本不用了解这么深,只有当你到了一定层次,需要了解jvm内部运行机制,或者高并发多线程下,你写的代码对内存有影响,你想做性能优化.等等等等,一句话,当你想深入了解java对象在内存中,如何存储,或者每个对象占用多大空间时,你会感谢这篇文章 本文主要分析jvm中的情况,实验环境为64位window10系统.JDK1.8,使用JProfiler进行结论验证 很多描述以及 概念是基于你懂基本java知识的,如果你看起来有点吃力,要加油咯 本片更偏重验证,更多理论,请参考:http

  • Java反射(Class类,Class对象获取)

    目录 Java反射超详解 1.反射基础 1.1Class类 1.2类加载 2.反射的使用 2.1Class对象的获取 2.2Constructor类及其用法 2.3Field类及其用法 Java反射超详解 1.反射基础 Java反射机制是在程序的运行过程中,对于任何一个类,都能够知道它的所有属性和方法:对于任意一个对象,都能够知道它的任意属性和方法,这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制. Java反射机制主要提供以下这几个功能: 在运行时判断任意一个对象所属的类

随机推荐