内存五大区
内存布局
当程序运行时,系统会开辟三个区,分别是:内核区
、程序使用的内存五大区
和保留区
。
操作系统分为两种运行级别,分别是内核态
与用户态
。以4GB手机为例,系统将其中的3GB
给了五大区+保留区
,剩余的1GB
给内核区
使用,如下所示
- 内核区:系统用来进行
内核处理操作
的区域,主要是系统内核间的消息处理。 - 五大区:内存五大区
-
栈区
:存储函数
、方法
,内存地址一般以0x7
开头 -
堆区
:存储通过alloc
分配的对象、block copy
等,内存地址一般以0x6
开头 -
BSS段
:未初始化的全局变量
、静态变量
,内存地址一般以0x1
开头 -
数据段
: 初始化的全局变量
、静态变量
,内存地址一般以0x1
开头 -
text段
:程序代码,加载到内存中
- 保留区:预留给系统处理
nil
等
这里有个疑问,为什么五大区的最后内存地址是从0x00400000
开始的?其主要原因是0x00000000
表示nil,不能直接用nil表示一个段,所以单独给了一段内存用于处理nil等情况。
面试题
通过
static
修饰的成员变量不占内存,这句话怎么理解?
static
修饰的成员变量
在全局区
,而结构体
分配的内存是在堆区
,所谓的不占内存是不占用申请的堆区内存空间
,而占用的是全局区
的内存空间。栈区的内存是如何定位的?
通过sp寄存器
定位,sp为栈顶。
内存管理方案
iOS中内存管理方法,大致可以分为手动管理MRC
和自动管理ARC
。
MRC
对象通过引用计数判断是否销毁,需要手动调用对象的retain
、release
、autorelease
等方法,维护对象引用计数
- 对象
被创建时
,引用计数为1
- 调用对象的
retain
方法,引用计数+1
- 调用对象的
release
方法,引用计数-1
-
autorelease
是一个特殊的release,有用延后释放。调用对象的autorelease方法,对象会加入到自动释放池中,最迟会在主循环结束前释放,依赖于Runloop
- 当对象
引用计数为0
,系统将销毁此对象。
ARC
ARC为自动引用计数管理,属于编译器的一种特性,在WWDC2011
和iOS5
时代被引入
- 引用计数的规则和MRC手动管理一致
- 无需手动调用
retain
、release
、autorelease
等方法维护对象引用计数 - 编译器会在适当的地方自动插入
retain
、release
方法。
除了上述的ARC
和MRC
,内存管理方法中还包括几个重要的内容:
-
Tagged Pointer
:专门用于存储小的对象,例如:NSNumber
、NSIndexPath
、NSDate
、NSString
-
NonpointerISA
:非纯指针类型的isa,isa中包含了类信息
、对象的引⽤计数
等 -
SideTables
:散列表
,主要包含引用计数表
和弱引用表
。
WWDC看TaggedPointer
按照苹果官方的说法,即使我们的app不做任何优化
,运行速度也会变快
,那么苹果底层做了什么优化来作为支撑呢? 今天,我们就一探究竟。
苹果WWDC2020关于 Objective-C 运行时做出的更改
Tagged Pointer Format Changes
什么是Tagged Pointer?
以NSString
为例,读取一个常规的NSString
,通过栈区存储的指针地址
找到堆区空间
,然后从堆区读取到字符串的值,整个读取流程效率较低。所以,系统对其进行优化,如果NSString
存储的字符串长度较短,会使用Tagged Pointer
存储。
Tagged Pointer
也是一个指针,指针中包含Tagged
标记,用于区分存储的数据类型。同时将值也存储在指针中,通过位运算
将其编码成一个指针格式。
Tagged Pointer
的读取,只需要将指针解码
,通过tagget
标记按不同类型规则进行读取即可,这样即节省内存空间
,同时提升读取效率
。
测试NSString
的内存管理
- (void)viewDidLoad {
[super viewDidLoad];
NSString *str1 = @"kc";
NSLog(@"%p-%@-%@",str1,str1,str1.class);
NSString *str2 = [NSString stringWithString:@"kc"];
NSLog(@"%p-%@-%@",str2,str2,str2.class);
NSString *str3 = [NSString stringWithFormat:@"kc"];
NSLog(@"%p-%@-%@",str3,str3,str3.class);
NSString *str4 = [NSString stringWithFormat:@"1234567890"];
NSLog(@"%p-%@-%@",str4,str4,str4.class);
}
// 控制台打印
0x10e516060-kc-__NSCFConstantString
0x10e516060-kc-__NSCFConstantString
0xa0000000000636b2-kc-NSTaggedPointerString
0x6000027c8fe0-1234567890-__NSCFString
对打印结果进行总结,NSString
的内存管理主要分为3种
-
__NSCFConstantString
:字符串常量,是一种编译时常量
,retainCount
值很大,对其操作不会引起引用计数变化,存储在字符串常量区
-
__NSCFString
:是在运行时创建的NSString
子类,创建后引用计数会加1,存储在堆上
-
NSTaggedPointerString
:标签指针
是苹果在64位
环境下对NSString
、NSNumber
等对象做的优化。
对于NSString对象来说
- 当字符串是由
数字
、英文字母
组合且长度小于等于9
时,会自动成为NSTaggedPointerString
类型,存储在常量区
- 当有
中文
或者其他特殊符号
时,会直接成为__NSCFString
类型,存储在堆区
x86-64(模拟器64位)下的TaggedPointer
- 在
objc4-818.2
源码中,找到Tagged Pointer
的解码方法。进入_objc_decodeTaggedPointer
函数
static inline uintptr_t
_objc_decodeTaggedPointer(const void * _Nullable ptr)
{
// 调用_objc_decodeTaggedPointer_noPermute函数->返回指针
uintptr_t value = _objc_decodeTaggedPointer_noPermute(ptr);
#if OBJC_SPLIT_TAGGED_POINTERS
uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
value &= ~(_OBJC_TAG_INDEX_MASK << _OBJC_TAG_INDEX_SHIFT);
value |= _objc_obfuscatedTagToBasicTag(basicTag) << _OBJC_TAG_INDEX_SHIFT;
#endif
return value;
}
- 查看
_objc_decodeTaggedPointer_noPermute
函数
static inline uintptr_t
_objc_decodeTaggedPointer_noPermute(const void * _Nullable ptr)
{
uintptr_t value = (uintptr_t)ptr;
// 判断OBJC_SPLIT_TAGGED_POINTERS,符合条件进行位运算
#if OBJC_SPLIT_TAGGED_POINTERS
if ((value & _OBJC_TAG_NO_OBFUSCATION_MASK) == _OBJC_TAG_NO_OBFUSCATION_MASK)
return value;
#endif
// 和objc_debug_taggedpointer_obfuscator进行按位异或
// objc_debug_taggedpointer_obfuscator 进行混淆,可以理解为随机数
return value ^ objc_debug_taggedpointer_obfuscator;
}
-
objc_debug_taggedpointer_obfuscator
的赋值:在dyld
读取image
时调用_read_images
函数,里面包含对initializeTaggedPointerObfuscator
函数的调用,对Tagged Pointer
进行初始化。 - 系统生成的
Tagged Pointer
是编码后的,我们要想了解它的结构,需要对其进行解码
。 -
objc_debug_taggedpointer_obfuscator
为全局静态变量
,我们可以在程序中使用extern
修饰将其导出,自己实现一个解码函数
,使用相同的值,将指针再次按位异或
即可还原。
extern uintptr_t objc_debug_taggedpointer_obfuscator;
uintptr_t
kc_objc_decodeTaggedPointer(id ptr) {
return (uintptr_t)ptr ^ objc_debug_taggedpointer_obfuscator;
}
案例测试代码:
- (void)viewDidLoad {
[super viewDidLoad];
NSString *str = [NSString stringWithFormat:@"kc"];
NSLog(@"%p-%@-%@ - 0x%lx",str,str,str.class,kc_objc_decodeTaggedPointer(str));
}
// 控制台打印
0xa0000000000636b2-kc-NSTaggedPointerString - 0xa0000000000636b2
-
lldb
使用p/t
命令,查看二进制形式
(lldb) p/t 0xa0000000000636b2
// 0b表示二进制; 高地址第一位1表示该isa为Tagged Pointer类型
(unsigned long) $0 = 0b1010000000000000000000000000000000000000000001100011011010110010
- 通过位运算,获取
有效负载
(lldb) p/t $0 >> 4
(unsigned long) $1 = 0b0000101000000000000000000000000000000000000000000110001101101011
// 低地址最后16位,每8位进行一次打印
(lldb) po 0b01101011
107
(lldb) po 0b01100011
99
// 里面存储的内容,其实就是字符的assic码
(lldb) po (char)107
'k'
(lldb) po (char)99
'c'
- 将
isa
转为二进制,高地址的前4位0b1010
,第一位表示该isa
为Tagged Pointer
类型,后面三位010
表示Tagged Pointer
所存储的类型
// 2表示上面str是字符串类型
(lldb) po 0b010
2
- 对应
objc源码
中的类型,上一步打印的2
表示str
是字符串类型
// 60-bit payloads
OBJC_TAG_NSAtom = 0,
OBJC_TAG_1 = 1,
OBJC_TAG_NSString = 2,
OBJC_TAG_NSNumber = 3,
OBJC_TAG_NSIndexPath = 4,
OBJC_TAG_NSManagedObjectID = 5,
OBJC_TAG_NSDate = 6,
......
NSNumber
类型自己尝试......
arm64(真机64位)下的TaggedPointer
objc4-818.2
源码中,查看Tagged Pointer
在真机上的编码方法
static inline void * _Nonnull
_objc_encodeTaggedPointer(uintptr_t ptr)
{
uintptr_t value = (objc_debug_taggedpointer_obfuscator ^ ptr);
#if OBJC_SPLIT_TAGGED_POINTERS
if ((value & _OBJC_TAG_NO_OBFUSCATION_MASK) == _OBJC_TAG_NO_OBFUSCATION_MASK)
return (void *)ptr;
uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
uintptr_t permutedTag = _objc_basicTagToObfuscatedTag(basicTag);
value &= ~(_OBJC_TAG_INDEX_MASK << _OBJC_TAG_INDEX_SHIFT);
value |= permutedTag << _OBJC_TAG_INDEX_SHIFT;
#endif
return (void *)value;
}
- 实现真机环境编码解码函数
#define kc_OBJC_TAG_INDEX_MASK 0x7UL
#define kc_OBJC_TAG_INDEX_SHIFT 0
extern uint8_t objc_debug_tag60_permutations[8];
uintptr_t kc_objc_obfuscatedTagToBasicTag(uintptr_t tag) {
for (unsigned i = 0; i < 7; i++)
if (objc_debug_tag60_permutations[i] == tag)
return i;
return 7;
}
uintptr_t
kc_objc_decodeTaggedPointer(id ptr) {
uintptr_t value = (uintptr_t)ptr ^ objc_debug_taggedpointer_obfuscator;
uintptr_t basicTag = (value >> kc_OBJC_TAG_INDEX_SHIFT) & kc_OBJC_TAG_INDEX_MASK;
value &= ~(kc_OBJC_TAG_INDEX_MASK << kc_OBJC_TAG_INDEX_SHIFT);
value |= kc_objc_obfuscatedTagToBasicTag(basicTag) << kc_OBJC_TAG_INDEX_SHIFT;
return value;
}
static inline uintptr_t kc_objc_basicTagToObfuscatedTag(uintptr_t tag) {
return objc_debug_tag60_permutations[tag];
}
void * kc_objc_encodeTaggedPointer(uintptr_t ptr) {
uintptr_t value = (objc_debug_taggedpointer_obfuscator ^ ptr);
uintptr_t basicTag = (value >> kc_OBJC_TAG_INDEX_SHIFT) & kc_OBJC_TAG_INDEX_MASK;
uintptr_t permutedTag = kc_objc_basicTagToObfuscatedTag(basicTag);
value &= ~(kc_OBJC_TAG_INDEX_MASK << kc_OBJC_TAG_INDEX_SHIFT);
value |= permutedTag << kc_OBJC_TAG_INDEX_SHIFT;
return (void *)value;
}
- 继续上面字符串案例,使用真机运行
- (void)viewDidLoad {
[super viewDidLoad];
NSString *str = [NSString stringWithFormat:@"kc"];
NSLog(@"%p-%@-%@ - 0x%lx",str,str,str.class,kc_objc_decodeTaggedPointer(str));
}
// 控制台打印
0x800000000031b592-kc-NSTaggedPointerString - 0x800000000031b592
// lldb使用p/t命令,查看二进制形式
(lldb) p/t 0x800000000031b592
// 和模拟器的区别,高地址第一位依然表示该isa为Tagged Pointer类型;
// 但类型的位置发生变化,存储在低地址最后三位,即010
(unsigned long) $0 = 0b1000000000000000000000000000000000000000001100011011010110010010
// 打印低地址最后三位,查看类型
(lldb) po 0b010
2 //表示字符串类型
// 010类型前面4位0010,即低地址4~7位,表示字符串长度,查看长度
(lldb) po 0b0010
2 //表示字符串长度
- 添加
NSNumber
类型
- (void)viewDidLoad {
[super viewDidLoad];
NSNumber *number = @6;
NSLog(@"%p-%@-%@ - 0x%lx",number,number,number.class,kc_objc_decodeTaggedPointer(number));
NSNumber *number1 = @(6.0);
NSLog(@"%p-%@-%@ - 0x%lx",number1,number1,number1.class,kc_objc_decodeTaggedPointer(number1));
}
// 控制台打印
0x8000000000000313-6 -__NSCFNumber- 0x8000000000000313
0x800000000000032b-6 -__NSCFNumber- 0x800000000000032b
// lldb使用p/t命令,查看二进制形式
(lldb) p/t 0x8000000000000313
// 低地址最后三位011表示NSNumber的类型
// 低地址4~7位,分别存储0010和0101,表示存储的基本数据类型。
// 例如:int、long、double、float等。
(unsigned long) $0 = 0b1000000000000000000000000000000000000000000000000000001100010011
(lldb) p/t 0x800000000000032b
(unsigned long) $1 = 0b1000000000000000000000000000000000000000000000000000001100101011
// 打印低地址4~7位,查看NSNumber数据类型
(lldb) po 0b0010
2 //表示int型
(lldb) po 0b0101
5 //表示double型
NSNumber
枚举值在objc源码
中未定义,只能通过代码测试对应的类型:char:0
、short:1
、int:2
、long:3
、float:4
、double:5
NSIndexPath
、NSDate
类型自己尝试......
关闭混淆
真机环境探索结构
需要我们自己实现编码解码
的代码,其实这里还有更简单的方式。
- 通过源码发现,
初始化时
如果不符合混淆条件,objc_debug_taggedpointer_obfuscator
会被设置为0
static void
initializeTaggedPointerObfuscator(void)
{
if (!DisableTaggedPointerObfuscation) {
// Pull random data into the variable, then shift away all non-payload bits.
arc4random_buf(&objc_debug_taggedpointer_obfuscator,
sizeof(objc_debug_taggedpointer_obfuscator));
objc_debug_taggedpointer_obfuscator &= ~_OBJC_TAG_MASK;
#if OBJC_SPLIT_TAGGED_POINTERS
// The obfuscator doesn't apply to any of the extended tag mask or the no-obfuscation bit.
objc_debug_taggedpointer_obfuscator &= ~(_OBJC_TAG_EXT_MASK | _OBJC_TAG_NO_OBFUSCATION_MASK);
// Shuffle the first seven entries of the tag permutator.
int max = 7;
for (int i = max - 1; i >= 0; i--) {
int target = arc4random_uniform(i + 1);
swap(objc_debug_tag60_permutations[i],
objc_debug_tag60_permutations[target]);
}
#endif
} else {
// Set the obfuscator to zero for apps linked against older SDKs,
// in case they're relying on the tagged pointer representation.
// 不符合混淆条件,置为0
objc_debug_taggedpointer_obfuscator = 0;
}
}
- 查看
DisableTaggedPointerObfuscation
混淆,它的值取决于OBJC_DISABLE_TAG_OBFUSCATION
环境变量的设置
OPTION( DisableTaggedPointerObfuscation, OBJC_DISABLE_TAG_OBFUSCATION, "disable obfuscation of tagged pointers")
- 测试项目中添加
OBJC_DISABLE_TAG_OBFUSCATION
环境变量,将其设置为YES
。即关闭混淆
- 运行工程,查看控制台打印。直接获得未编码的
isa
- (void)viewDidLoad {
[super viewDidLoad];
NSNumber *number = @6;
NSLog(@"%p-%@-%@ - 0x%lx",number,number,number.class,(uintptr_t)number);
NSNumber *number1 = @(6.0);
NSLog(@"%p-%@-%@ - 0x%lx",number1,number1,number1.class,(uintptr_)number1);
}
// 控制台打印
0x8000000000000313-6 -__NSCFNumber- 0x8000000000000313
0x800000000000032b-6 -__NSCFNumber- 0x800000000000032b
TaggedPointer面试题
下面代码运行结果如何?是否会产生崩溃?
- (void)viewDidLoad {
[super viewDidLoad];
[self taggedPointerDemo];
}
- (void)taggedPointerDemo {
self.queue = dispatch_queue_create("com.lg.cn", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i<10000; i++) {
dispatch_async(self.queue, ^{
self.nameStr = [NSString stringWithFormat:@"kc"];
NSLog(@"%@",self.nameStr);
});
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
for (int i = 0; i<100000; i++) {
dispatch_async(self.queue, ^{
self.nameStr = [NSString stringWithFormat:@"kc_和谐学习不急不躁"];
NSLog(@"%@",self.nameStr);
});
}
}
taggedPointerDemo
的运行,不会出现任何问题。因为字符串kc
会被优化成NSTaggedPointerString
类型。
touchesBegan
的运行,会导致程序崩溃。因为字符串中包含中文
,所以使用__NSCFString
类型。它的值存储在堆区
,赋值的过程本质上对旧值release
,对新值retain
。当多线程执行时,可能出现多次release
造成过度释放
,一些野指针的操作导致程序崩溃。
- 在
objc源码
中查找rootRetain
函数
如果是TaggedPointer
的isa
,直接返回。不用进行后面的新旧值
的retain
和release
。
- 查看
rootRelease
函数
判断如果是TaggedPointer
的isa
,直接返回false
Tagged Pointer 小结
-
Tagged Pointer
专门用于存储小的对象。例如NSNumber
、NSDate
、NSString
、NSIndexPath
- 指针由
标志+值+扩展+类型
组成,通过混淆编码成指针地址; - 使用
Tagged Pointer
类型的好处,节省内存空间,提升读取效率; -
Tagged Pointer
触发retain
和release
直接返回,这意味着它不需要ARC
进行管理,而是直接被系统回收释放
; -
Tagged Pointer
的内存并不存储在堆中
,而是在常量区
中,也不需要malloc
和free
,所以可以直接读取,相比存储在堆区的数据读取,效率上快了3倍
左右。创建的效率相比堆区
快了近100倍
左右 - 综合来说
taggedPointer
的内存管理方案,比常规的内存管理,要快很多。
Tagged Pointer
的64位地址中,前4位代表类型
,后4位主要适用于系统做一些处理,中间56位
用于存储值
优化内存建议:对于NSString
来说,当字符串较小时,建议直接通过@""
初始化,因为存储在常量区
,可以直接进行读取,会比WithFormat
初始化方式更加快速。
retain&release
nonpointer
:表示是否对isa
指针开启指针优化
0:纯isa指针,
1:不止是类对象地址。isa 中包含了类信息
、对象的引用计数
等。
和Tagged Pointer
类似, NONPOINTER_ISA
也是对isa
的存储位
的优化处理,以使其64位能更充分的得到利用,保存相关数据而不是空着浪费掉。
其中shiftcls
和PalLoad
相似,用来承载有效数据。
对象的引用计数存储在isa
中的extra_rc
extra_rc
表示该对象的引⽤计数值,实际上是引⽤计数值-1
。例如,如果对象的引⽤计数为10,那么extra_rc为9。如果引⽤计数⼤于 10,则需要使⽤到下⾯的has_sidetable_rc
。
has_sidetable_rc
当对象引⽤计数⼤于10时,则借⽤该变量存储进位
。此时会配合散列表SideTables
进行存储。
- 在
objc源码
中查看rootRetain
函数
ALWAYS_INLINE id
objc_object::rootRetain(bool tryRetain, objc_object::RRVariant variant) {
//1、判断如果是TaggedPointer,什么都不处理,直接返回
if (slowpath(isTaggedPointer()))
return (id)this;
bool sideTableLocked = false;
bool transcribeToSideTable = false;
isa_t oldisa;
isa_t newisa;
oldisa = LoadExclusive(&isa.bits);
if (variant == RRVariant::FastOrMsgSend) {
// These checks are only meaningful for objc_retain()
// They are here so that we avoid a re-load of the isa.
if (slowpath(oldisa.getDecodedClass(false)->hasCustomRR())) {
ClearExclusive(&isa.bits);
if (oldisa.getDecodedClass(false)->canCallSwiftRR()) {
return swiftRetain.load(memory_order_relaxed)((id)this);
}
return ((id(*)(objc_object *, SEL))objc_msgSend)(this, @selector(retain));
}
}
if (slowpath(!oldisa.nonpointer)) {
// a Class is a Class forever, so we can perform this check once
// outside of the CAS loop
//2、如果是纯isa,判断如果是一个类,也不需要Retain操作
if (oldisa.getDecodedClass(false)->isMetaClass()) {
ClearExclusive(&isa.bits);
return (id)this;
}
}
do {
transcribeToSideTable = false;
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
//3、如果是纯isa,使用散列表,进行Retain操作
ClearExclusive(&isa.bits);
if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
else return sidetable_retain(sideTableLocked);
}
// don't check newisa.fast_rr; we already called any RR overrides
if (slowpath(newisa.isDeallocating())) {
//4、如果当前isa正在释放,不需要Retain操作
ClearExclusive(&isa.bits);
if (sideTableLocked) {
ASSERT(variant == RRVariant::Full);
sidetable_unlock();
}
if (slowpath(tryRetain)) {
return nil;
} else {
return (id)this;
}
}
//5、通过bits对RC_ONE进行Retain操作,引用计数+1,将状态赋值
carryuintptr_t carry;
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
if (slowpath(carry)) {
// newisa.extra_rc++ overflowed
if (variant != RRVariant::Full) {
ClearExclusive(&isa.bits);
return rootRetain_overflow(tryRetain);
}
// Leave half of the retain counts inline and
// prepare to copy the other half to the side table.
if (!tryRetain && !sideTableLocked) sidetable_lock();
//6、存储已满,修改一些标记,设置isa的extra_rc和has_sidetable_rc
//RC_HALF表示砍半,将一半存储在
extra_rcsideTableLocked = true;
transcribeToSideTable = true;
newisa.extra_rc = RC_HALF;
newisa.has_sidetable_rc = true;
}
} while (slowpath(!StoreExclusive(&isa.bits, &oldisa.bits, newisa.bits)));
if (variant == RRVariant::Full) {
if (slowpath(transcribeToSideTable)) {
// Copy the other half of the retain counts to the side table.
//7、将另一半存储到散列表SideTable中
sidetable_addExtraRC_nolock(RC_HALF);
}
if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
} else {
ASSERT(!transcribeToSideTable); ASSERT(!sideTableLocked);
}
return (id)this;
}
retain流程图
- 查看
rootRelease
函数
ALWAYS_INLINE bool
objc_object::rootRelease(bool performDealloc, objc_object::RRVariant variant) {
//1、判断如果是TaggedPointer,什么都不处理,直接返回
if (slowpath(isTaggedPointer())) return false;
bool sideTableLocked = false;
isa_t newisa, oldisa;
oldisa = LoadExclusive(&isa.bits);
if (variant == RRVariant::FastOrMsgSend) {
// These checks are only meaningful for objc_release()
// They are here so that we avoid a re-load of the isa.
if (slowpath(oldisa.getDecodedClass(false)->hasCustomRR())) {
ClearExclusive(&isa.bits);
if (oldisa.getDecodedClass(false)->canCallSwiftRR()) {
swiftRelease.load(memory_order_relaxed)((id)this);
return true;
}
((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(release));
return true;
}
}
if (slowpath(!oldisa.nonpointer)) {
// a Class is a Class forever, so we can perform this check once
// outside of the CAS loop
//2、如果是纯isa,判断如果是一个类,也不需要Release操作
if (oldisa.getDecodedClass(false)->isMetaClass()) {
ClearExclusive(&isa.bits);
return false;
}
}
retry:
do {
newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
//3、如果是纯isa,使用散列表,进行Release操作
ClearExclusive(&isa.bits);
return sidetable_release(sideTableLocked, performDealloc);
}
if (slowpath(newisa.isDeallocating())) {
//4、如果当前isa正在释放,不需要Release操作
ClearExclusive(&isa.bits);
if (sideTableLocked) {
ASSERT(variant == RRVariant::Full);
sidetable_unlock();
}
return false;
}
// don't check newisa.fast_rr; we already called any RR overrides
//5、通过bits对RC_ONE进行Release操作,引用计数-1,将状态赋值
carryuintptr_t carry;
newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry); // extra_rc--
if (slowpath(carry)) {
// don't ClearExclusive()
//6、如果extra_rc减空,进入underflow代码流程
goto underflow;
}
} while (slowpath(!StoreReleaseExclusive(&isa.bits, &oldisa.bits, newisa.bits)));
if (slowpath(newisa.isDeallocating()))
goto deallocate;
if (variant == RRVariant::Full) {
if (slowpath(sideTableLocked)) sidetable_unlock();
} else {
ASSERT(!sideTableLocked);
}
return false;
underflow:
// newisa.extra_rc-- underflowed: borrow from side table or deallocate
// abandon newisa to undo the decrement
newisa = oldisa;
if (slowpath(newisa.has_sidetable_rc)) {
//7、判断当前已使用散列表存储
if (variant != RRVariant::Full) {
ClearExclusive(&isa.bits);
return rootRelease_underflow(performDealloc);
}
// Transfer retain count from side table to inline storage.
if (!sideTableLocked) {
ClearExclusive(&isa.bits);
sidetable_lock();
sideTableLocked = true;
// Need to start over to avoid a race against
// the nonpointer -> raw pointer transition.
oldisa = LoadExclusive(&isa.bits);
goto retry;
}
// Try to remove some retain counts from the side table.
//从散列表中取出一半
auto borrow = sidetable_subExtraRC_nolock(RC_HALF);
//如果散列表中取空了,标记emptySideTable
bool emptySideTable = borrow.remaining == 0; // we'll clear the side table if no refcounts remain there
//判断从散列表中取出内容
if (borrow.borrowed > 0) {
// Side table retain count decreased.
// Try to add them to the inline count.
bool didTransitionToDeallocating = false;
//进行-1操作,赋值extra_rc
//通过emptySideTable标记,修改has_sidetable_rc
newisa.extra_rc = borrow.borrowed - 1; // redo the original decrement too
newisa.has_sidetable_rc = !emptySideTable;
//存储到isa的bits中
bool stored = StoreReleaseExclusive(&isa.bits, &oldisa.bits, newisa.bits);
if (!stored && oldisa.nonpointer) {
// Inline update failed.
// Try it again right now. This prevents livelock on LL/SC
// architectures where the side table access itself may have
// dropped the reservation.
//存储失败的补救处理
uintptr_t overflow;
newisa.bits = addc(oldisa.bits, RC_ONE * (borrow.borrowed-1), 0, &overflow);
newisa.has_sidetable_rc = !emptySideTable;
if (!overflow) {
stored = StoreReleaseExclusive(&isa.bits, &oldisa.bits, newisa.bits);
if (stored) {
didTransitionToDeallocating = newisa.isDeallocating();
}
}
}
if (!stored) {
// Inline update failed.
// Put the retains back in the side table.
ClearExclusive(&isa.bits);
sidetable_addExtraRC_nolock(borrow.borrowed);
oldisa = LoadExclusive(&isa.bits);
goto retry;
}
// Decrement successful after borrowing from side table.
if (emptySideTable)
sidetable_clearExtraRC_nolock();
if (!didTransitionToDeallocating) {
if (slowpath(sideTableLocked)) sidetable_unlock();
return false;
}
} else {
// Side table is empty after all. Fall-through to the dealloc path.
}
}
deallocate:
// Really deallocate.
//8、进入deallocate代码流程
ASSERT(newisa.isDeallocating());
ASSERT(isa.isDeallocating());
if (slowpath(sideTableLocked)) sidetable_unlock();
__c11_atomic_thread_fence(__ATOMIC_ACQUIRE);
if (performDealloc) {
//调用对象的dealloc方法
((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
}
return true;
}