Swift类和对象的底层探索分析
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>引言</li><li>1. 对象</li><ul class="second_class_ul"><li>1.1 上层代码中查找</li><ul class="third_class_ul"><li>1.1.1 查找对象调用方法</li><li>1.1.2 设置符号断点</li></ul><li>1.2 swift_allocObject</li><ul class="third_class_ul"></ul><li>1.3 swift_showAlloc</li><ul class="third_class_ul"></ul><li>1.4 查看HeapObject结构体</li><ul class="third_class_ul"></ul><li>1.5 对象内存大小计算</li><ul class="third_class_ul"></ul><li>1.6 总结</li><ul class="third_class_ul"></ul></ul><li>2. 类</li><ul class="second_class_ul"><li>2.1 查找HeapMetadata</li><ul class="third_class_ul"></ul><li>2.2. TargetHeapMetaData</li><ul class="third_class_ul"></ul><li>2.3. TargetMetaData</li><ul class="third_class_ul"></ul><li>2.4. TargetClassMetadata</li><ul class="third_class_ul"></ul><li>2.5. TargetAnyClassMetadata</li><ul class="third_class_ul"></ul></ul></ul></div><p class="maodian"></p><h2>引言</h2><p>在上文已经了解了SIL,接下来主要通过Swift源码和SIL剖析底层。本文主要通过底层源码探索类和对象在底层的结构</p>
<p>主要内容:</p>
<ul><li>对象</li><li>类</li></ul>
<p class="maodian"></p><h2>1. 对象</h2>
<p>通过源码中探索Swift对象创建过程以及最终得到的对象结构。</p>
<p class="maodian"></p><h3>1.1 上层代码中查找</h3>
<p>通过符号断点调试来查找底层调用方法</p>
<p>源码:</p>
<div class="jb51code"><pre class="brush:cpp;">class WYStudent {
var age: Int = 18
var name: String = "WY"
}
var stu = WYStudent();
</pre></div>
<p class="maodian"></p><h4>1.1.1 查找对象调用方法</h4>
<p>通过断点查看发现是通过__allocating_init()方法实现对象的创建</p>
<p>添加断点</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202209/20220901085555027.jpg" /></p>
<p>查看调用方法</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202209/20220901085555028.jpg" /></p>
<p class="maodian"></p><h4>1.1.2 设置符号断点</h4>
<p>符号断点:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202209/20220901085555029.jpg" /></p>
<p>查看:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202209/20220901085555030.jpg" /></p>
<p>说明:</p>
<ul><li>在上面SIL的认识中已经知道了对象是通过__allocating_init()来创建的,在此处打断点查看</li><li>在__allocating_init()方法中可以看到会调用swift_allocObject()方法</li><li>因此接下来就需要在源码中查看该方法</li><li>__allocating_init()方法中做了两件事<ul><li>调用swift_allocObject创建对象</li><li>调用init()初始化对象,这个init方法是类默认提供的,也是默认调用的</li></ul></li></ul>
<p class="maodian"></p><h3>1.2 swift_allocObject</h3>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202209/20220901085555031.jpg" /></p>
<p>说明:</p>
<ul><li>通过swift_slowAlloc分配内存,并进行内存字节对齐,传入开辟的内存空间大小和对齐位数</li><li>通过HeapObject方法构造一个HeapObject对象,并且绑定到object上</li><li>因此此时的object就是一个heapObject对象</li><li>函数的返回值是HeapObject类型,所以当前对象的内存结构就是HeapObject的内存结构</li></ul>
<p class="maodian"></p><h3>1.3 swift_showAlloc</h3>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202209/20220901085555032.jpg" /></p>
<div class="jb51code"><pre class="brush:cpp;">// Apple malloc is always 16-byte aligned.
#define MALLOC_ALIGN_MASK 15
</pre></div>
<p>说明:</p>
<ul><li>通过swift_slowAlloc用来分配内存空间</li><li>这里会通过对齐位数来判断使用哪种方法来分配空间</li><li>最小的对齐位数是16字节,如果传入的位数小于16字节,那么就是用16字节对齐,也就是使用malloc方法</li><li>如果大于16字节位数,那么使用AlignedAlloc方法</li></ul>
<p class="maodian"></p><h3>1.4 查看HeapObject结构体</h3>
<p>结构体</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202209/20220901085555033.jpg" /></p>
<p>refCounts查看:</p>
<div class="jb51code"><pre class="brush:cpp;">typedef RefCounts<InlineRefCountBits> InlineRefCounts;
//是一个类,所以它的对象就是8个字节
class RefCounts {
std::atomic<RefCountBits> refCounts;//引用计数
...
}
</pre></div>
<p>说明:</p>
<ul><li>结构体内包含一个成员,metadata</li><li>HeapObject()初始化器,会初始化metadata和refCounts,因此对象中会有这两种属性</li><li>其中metadata类型是HeapMetadata,是一个指针类型,占8字节,其实它就是类信息</li><li>refCounts是引用计数,也占有8个字节</li><li>refCounts的类型是InlineRefCounts</li><li>而InlineRefCounts是一个类RefCounts的别名</li><li>RefCounts是一个类,所以refCounts占8个字节</li></ul>
<p class="maodian"></p><h3>1.5 对象内存大小计算</h3>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202209/20220901085555034.jpg" /></p>
<p>说明:</p>
<ul><li>metadata占8个字节</li><li>refCounts占8个字节</li><li>再加上age的8个字节</li><li>name占8个字节</li><li>所以总共是40个字节</li></ul>
<p class="maodian"></p><h3>1.6 总结</h3>
<p>实例对象的底层结构是HeapObject结构体</p>
<p>默认16字节内存大小,metadata 8字节 + refCounts 8字节</p>
<p>metadata是类信息结构,下面会分析</p>
<p>refCounts是引用计数,后面也会详细分析</p>
<p>Swift中对象的内存分配流程是:</p>
<blockquote><p>__ allocating_init --> swift_allocObject_ --> _swift_allocObject --> swift_slowAlloc --> malloc</p></blockquote>
<p class="maodian"></p><h2>2. 类</h2>
<p>对象在底层中的结构是HeapObject结构体,其第一个属性为metadata,因此从这个属性出发来查看类的结构</p>
<p class="maodian"></p><h3>2.1 查找HeapMetadata</h3>
<p>代码:</p>
<div class="jb51code"><pre class="brush:csharp;">using HeapMetadata = TargetHeapMetaData<Inprocess>;
</pre></div>
<p>说明:</p>
<ul><li>上文可知对象结构体HeapObject包含有HeapMetadata结构体,对象通过它来查找对应的类信息</li><li>点击进入HeapMetadata的定义,发现它是TargetHeapMetaData类型的别名</li><li>并且接收了一个参数Inprocess</li></ul>
<p class="maodian"></p><h3>2.2. TargetHeapMetaData</h3>
<p>代码:</p>
<div class="jb51code"><pre class="brush:csharp;">//模板类型
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
};
</pre></div>
<p>说明:</p>
<ul><li>TargetHeapMetaData其本质是一个模板类型,其中定义了一些所需的数据结构</li><li>这个结构体中没有属性,只有初始化方法</li><li>初始化方法中传入了一个MetadataKind类型的参数,之后就可以返回TargetMetaData对象</li><li>同时可以看到这里传入的kind也就是上面的inprocess了</li><li>该初始化方法构造的对象需要通过该参数来确定</li></ul>
<p class="maodian"></p><h3>2.3. TargetMetaData</h3>
<p>代码:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202209/20220901085555035.jpg" /></p>
<p>说明:</p>
<ul><li>在TargetMetaData中可以看到有一个Kind属性,这是在构建对象时传入的那个参数</li></ul>
<p>查看MetadataKind</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202209/20220901085555036.jpg" /></p>
<p>说明:</p>
<ul><li>可以看到它是uint32_t类型</li></ul>
<p>类型</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202209/20220901085555037.jpg" /></p>
<p>说明:</p>
<ul><li>进入MetadataKind定义,里面有一个#include "MetadataKind.def"</li><li>点击进入,其中记录了所有类型的元数据</li></ul>
<p>getClassObject方法:</p>
<div class="jb51code"><pre class="brush:cpp;">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;
}
}
</pre></div>
<p>说明:</p>
<ul><li>在TargetMetaData结构体定义中有一个方法getClassObject,它就可以用来获取类对象,也就是类</li><li>在方法中的核心逻辑是通过kind来判断当前是哪种类型,之后返回</li><li>这里我们需要的是类类型,因此判断为MetadataKind::Class,就会返回ClassMetadata类型</li></ul>
<p>验证:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202209/20220901085555038.jpg" /></p>
<p>命令:</p>
<blockquote><p>po metadata->getKind()</p></blockquote>
<p>得到其kind是Class</p>
<blockquote><p>po metadata->getClassObject() + x/8g 0x0000000110efdc70</p></blockquote>
<p>这个地址中存储的是元数据信息!</p>
<p>说明:</p>
<ul><li>传递进来的Kind发现可以判断为类</li><li>通过方法调用最后得到的是一个类对象,也就是类</li><li>通过x/8g查看类信息,里面就是存储的元数据信息</li></ul>
<p>注意:</p>
<ul><li>TargetMetadata 和 TargetClassMetadata 本质上是一样的</li><li>因为在内存结构中,可以直接进行指针的转换,所以可以说,我们认为的结构体,其实就是TargetClassMetadata</li></ul>
<p class="maodian"></p><h3>2.4. TargetClassMetadata</h3>
<p>代码:</p>
<div class="jb51code"><pre class="brush:cpp;">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;
...
}
</pre></div>
<p>说明:</p>
<ul><li>包含了很多属性,这些都属于类结构信息</li><li>并且它继承自TargetAnyClassMetadata</li></ul>
<p class="maodian"></p><h3>2.5. TargetAnyClassMetadata</h3>
<p>代码:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202209/20220901085555039.jpg" /></p>
<p>说明:</p>
<ul><li>TargetAnyClassMetadata是所有的类结构,不单单是给Swift用的</li><li>继承自TargetHeapMetadata,这也证明类本身也是对象</li><li>提供有isa、superclass、cache、data,和OC的底层类结构完全一样</li></ul>
<p>以上就是Swift类和对象的底层探索分析的详细内容,更多关于Swift类和对象的资料请关注琼殿技术社区其它相关文章!</p>
<div class="art_xg">
<b>您可能感兴趣的文章:</b><ul><li>Swift 字符串类型及常用方法详解总结</li><li>Swift 5.1 之类型转换与模式匹配的教程详解</li><li>Swift如何使用类型擦除及自定义详解</li><li>详解Swift面向对象编程中的方法(method)</li><li>解析Swift语言面相对象编程中的继承特性</li></ul>
</div>
</div>
<!--endmain-->
頁:
[1]