type
status
date
slug
summary
tags
category
icon
password
在前面的文章中,已经对引用计数以及其在开发中的使用做了初步了解。在本篇中,我们将会深入阐述苹果对引用计数这个技术的底层实现。
本文涉及到的objc源码,来自于objc源码,版本是723,文中涉及大量源码,均做删减,主要对加锁部分进行删减。
导读:
- 对源码解读部分,若觉得源码冗余,每一步解读最后都有总结,可直接看总结。
- 【第五部分-图】总结了引用计数相关的图。
一、引用计数的存储
对于纯量类型的变量,是没有引用计数的,因为不是对象,其申请的变量存储在栈上,由系统来负责管理。
另外前面讲过的
Tagged Pointer
小对象,包括NSNumber
、NSString
、NSDate
等几个类的变量。它们也是存储在栈上,当然也没有引用计数。整理了下面表格:
关于对象类型的引用计数,其存在的地方有两个:
1.1 isa指针里的引用计数
首先,我们来观察对象类型中存放在优化后的
isa指针
,下面是出现过多次的isa指针
布局:1.2 Side Table里的引用计数
Side Table在系统中的结构如下:
而每一张Side Table中针对引用计数的部分如下:
二、引用计数管理
在了解了引用计数存储的地方之后,对引用计数管理的理解就更方便了。
2.1 管理引用计数的方法
下面,针对上面的方法,根据源码,进行一定的追踪。
2.2 retainCount
后续引用计数相关方法,只看优化过的指针,即只针对64位进行描述。
根据源码,其调用轨迹如下:
NSObject.mm
━retainCount()
┗━ rootRetainCount()
┗━ rootRetainCount()
retainCount总结
其中,针对后两种情况:
对于存在于Side Table中的引用计数,需要注意,其最低2位被占用,所以取出来,要右移2位。
2.3 retain
源码调用路径如下:
NSObject.mm
━retain()
┗━objc_retain()
┗━obj->retain()
┗━rootRetain()
retain总结:
Tagged Pointer对象,没有retain。
isa中extra_rc若未溢出,则累加1。如果溢出,则将isa和side table对半存储。
2.4 release
源码调用路径:
NSObject.mm
━release()
┗━ objc_release()
┗━ obj->release()
┗━ rootRelease()
下面是
release
方法的主要执行流程:release总结:
三、对象的销毁
3.1 dealloc方法
3.2 dealloc重写
3.3 dealloc源码
源码的执行流程如下:
━ dealloc
┗━_objc_rootDealloc —— <NSObject.mm>
┗━ rootDealloc —— <objc-object.h>
┗━ object_dispose —— <objc-runtime-new.mm>
┗━ objc_destructInstance、free —— <objc-runtime-new.mm>
我们跟踪方法
rootDealloc
:下面是具体销毁对象的流程:
是的,下一步就是进入
clearDeallocating()
方法。四、weak
weak有一个特性,在对象销毁的时候,指向该对象所有的weak指针都会置为nil,那么这个特性是如何在
dealloc
方法里体现的。4.1 weak
指针存储对象结构
要了解weak指针的处理,先要了解其对象结构。
weak指针存储在一个个
Side Table
对象中的weak_table
,这是一张hash
表。之后,在
weak_table
中,存储着一个个对象的entry
表,这也是个hash
表,每个entry
就存储着要销毁对象的所有弱引用地址。Side Table在系统有一组,可根据对象地址获取其对应的SideTable。
如下图所示:
4.2 weak
指针存储的hash表
上面说道
weak
指针的对象结构,如下图,展示的是weak
指针如何通过hash
表串联起来,进行存储的。4.3 源码
下面,我们就继续跟踪上面说到的
clearDeallocating()
方法,根据优化过的isa指针,其中调用的:━ clearDeallocating() —— <objc-object.h>
┗━ clearDeallocating_slow() —— <NSObject.mm>
更进一步,看看是如何清空
weak
指针的。weak指针处理总结:
五、图
5.1 对象结构图
5.2 存储图
参考
链接