type
status
date
slug
summary
tags
category
icon
password
在前面的文章中,已经对引用计数以及其在开发中的使用做了初步了解。在本篇中,我们将会深入阐述苹果对引用计数这个技术的底层实现。
本文涉及到的objc源码,来自于objc源码,版本是723,文中涉及大量源码,均做删减,主要对加锁部分进行删减。
导读:
- 对源码解读部分,若觉得源码冗余,每一步解读最后都有总结,可直接看总结。
- 【第五部分-图】总结了引用计数相关的图。
一、引用计数的存储
对于纯量类型的变量,是没有引用计数的,因为不是对象,其申请的变量存储在栈上,由系统来负责管理。
另外前面讲过的
Tagged Pointer
小对象,包括NSNumber
、NSString
、NSDate
等几个类的变量。它们也是存储在栈上,当然也没有引用计数。整理了下面表格:
![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Ff283ca8c-3a00-40dd-87c9-43c248d44a31%2F3b7abc61-b628-4849-bb80-d5b128aff904%2FUntitled.png?table=block&id=ec4dbc08-5fef-4da5-a650-fd152c983c9d&t=ec4dbc08-5fef-4da5-a650-fd152c983c9d&width=672&cache=v2)
关于对象类型的引用计数,其存在的地方有两个:
![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Ff283ca8c-3a00-40dd-87c9-43c248d44a31%2Fda0afb93-ff9c-467d-8e1f-c18cbfbf76de%2FUntitled.png?table=block&id=4a6935c1-31a5-44f6-93fd-976023f8db7b&t=4a6935c1-31a5-44f6-93fd-976023f8db7b&width=720&cache=v2)
1.1 isa指针里的引用计数
首先,我们来观察对象类型中存放在优化后的
isa指针
,下面是出现过多次的isa指针
布局:![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Ff283ca8c-3a00-40dd-87c9-43c248d44a31%2F2b475514-228b-4bad-b952-fb6eb9a9eb2c%2FUntitled.png?table=block&id=9e201def-379b-4ce2-9c26-674aa3bf80a2&t=9e201def-379b-4ce2-9c26-674aa3bf80a2&width=768&cache=v2)
1.2 Side Table里的引用计数
Side Table在系统中的结构如下:
![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Ff283ca8c-3a00-40dd-87c9-43c248d44a31%2F2f649057-a109-48fe-98f7-1c16e0f07000%2FUntitled.png?table=block&id=3a64bb07-680a-415d-914e-1addae57e54f&t=3a64bb07-680a-415d-914e-1addae57e54f&width=624&cache=v2)
而每一张Side Table中针对引用计数的部分如下:
![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Ff283ca8c-3a00-40dd-87c9-43c248d44a31%2F63ee9ce7-3fbf-4b83-b5bd-b4f46705f627%2FUntitled.png?table=block&id=daef031f-9846-4c56-a01d-d5b1f2cf0e76&t=daef031f-9846-4c56-a01d-d5b1f2cf0e76&width=768&cache=v2)
二、引用计数管理
在了解了引用计数存储的地方之后,对引用计数管理的理解就更方便了。
2.1 管理引用计数的方法
![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Ff283ca8c-3a00-40dd-87c9-43c248d44a31%2Ff4bf0d4c-ea3d-4c6d-a67d-60c5719dc9b2%2FUntitled.png?table=block&id=e89baa75-f0ba-455a-ac7d-f066d2b7b619&t=e89baa75-f0ba-455a-ac7d-f066d2b7b619&width=672&cache=v2)
下面,针对上面的方法,根据源码,进行一定的追踪。
2.2 retainCount
后续引用计数相关方法,只看优化过的指针,即只针对64位进行描述。
根据源码,其调用轨迹如下:
NSObject.mm
━retainCount()
┗━ rootRetainCount()
┗━ rootRetainCount()
retainCount总结
![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Ff283ca8c-3a00-40dd-87c9-43c248d44a31%2F8ccfd824-f3a0-499a-b333-91b85781b0f8%2FUntitled.png?table=block&id=258ba573-3f68-4585-b5a0-766fdaa705f3&t=258ba573-3f68-4585-b5a0-766fdaa705f3&width=816&cache=v2)
其中,针对后两种情况:
![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Ff283ca8c-3a00-40dd-87c9-43c248d44a31%2F97f2850e-68c8-47df-8822-4c5eb3c689f0%2FUntitled.png?table=block&id=057ad11f-4e48-440f-bbe5-455d88656292&t=057ad11f-4e48-440f-bbe5-455d88656292&width=816&cache=v2)
对于存在于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对半存储。
![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Ff283ca8c-3a00-40dd-87c9-43c248d44a31%2Fb123f3b2-71c9-4f7b-89dc-0d36d697e523%2FUntitled.png?table=block&id=08d662a9-1e55-4217-9352-43f80a81bf1d&t=08d662a9-1e55-4217-9352-43f80a81bf1d&width=816&cache=v2)
2.4 release
源码调用路径:
NSObject.mm
━release()
┗━ objc_release()
┗━ obj->release()
┗━ rootRelease()
下面是
release
方法的主要执行流程:release总结:
![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Ff283ca8c-3a00-40dd-87c9-43c248d44a31%2F49424c9e-b128-40a5-85b6-d87306a1a642%2FUntitled.png?table=block&id=40f69338-b6bd-4359-adbb-d2bdf9a2b952&t=40f69338-b6bd-4359-adbb-d2bdf9a2b952&width=864&cache=v2)
三、对象的销毁
3.1 dealloc方法
![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Ff283ca8c-3a00-40dd-87c9-43c248d44a31%2F999b95c9-fa15-41b7-b5a9-63281d46817a%2FUntitled.png?table=block&id=df895965-083d-47f1-baa6-87866c1853a1&t=df895965-083d-47f1-baa6-87866c1853a1&width=816&cache=v2)
3.2 dealloc重写
![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Ff283ca8c-3a00-40dd-87c9-43c248d44a31%2F07a4c2cd-b8d3-46ba-b84f-33c8bee59ea5%2FUntitled.png?table=block&id=352b8d2d-ee8f-46fe-88bc-d859f43d695b&t=352b8d2d-ee8f-46fe-88bc-d859f43d695b&width=768&cache=v2)
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。
如下图所示:
![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Ff283ca8c-3a00-40dd-87c9-43c248d44a31%2F398442bd-d58f-4186-85f5-89299a83a4b9%2FUntitled.png?table=block&id=3517f352-c48c-4705-af66-cff687cd816a&t=3517f352-c48c-4705-af66-cff687cd816a&width=1104&cache=v2)
4.2 weak
指针存储的hash表
上面说道
weak
指针的对象结构,如下图,展示的是weak
指针如何通过hash
表串联起来,进行存储的。![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Ff283ca8c-3a00-40dd-87c9-43c248d44a31%2F22466e09-801f-4383-8f15-5ff2bef7cc40%2FUntitled.png?table=block&id=fc2ea424-36ff-4ffb-af47-229a9d8ea450&t=fc2ea424-36ff-4ffb-af47-229a9d8ea450&width=960&cache=v2)
4.3 源码
下面,我们就继续跟踪上面说到的
clearDeallocating()
方法,根据优化过的isa指针,其中调用的:━ clearDeallocating() —— <objc-object.h>
┗━ clearDeallocating_slow() —— <NSObject.mm>
更进一步,看看是如何清空
weak
指针的。weak指针处理总结:
![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Ff283ca8c-3a00-40dd-87c9-43c248d44a31%2F31823858-b38e-468b-bd89-46557873c11c%2FUntitled.png?table=block&id=b1963095-cd1f-442a-bd81-3f68e86deebf&t=b1963095-cd1f-442a-bd81-3f68e86deebf&width=672&cache=v2)
五、图
5.1 对象结构图
![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Ff283ca8c-3a00-40dd-87c9-43c248d44a31%2F927a4d3d-b25a-4045-a27f-bd80ba0e93ef%2FUntitled.png?table=block&id=69b96b3d-8b70-4324-8e00-903f8dffdba8&t=69b96b3d-8b70-4324-8e00-903f8dffdba8&width=864&cache=v2)
5.2 存储图
![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Ff283ca8c-3a00-40dd-87c9-43c248d44a31%2F9b507753-0a9c-4a27-9f0a-07a403fd10af%2FUntitled.png?table=block&id=48912556-40a5-4893-b519-503e943a5687&t=48912556-40a5-4893-b519-503e943a5687&width=864&cache=v2)
参考
链接