OC内存管理之引用计数
Objective-C 的内存管理是通过引用计数实现的!若引用计数为正,则对象存活;若引用计数降为 0,运行时系统会释放它的内存!
在 objc
源码中使用散列表 SideTable
辅助管理对象的强引用计数和弱引用依赖:
//散列表 SideTable :主要用于辅助管理对象的 强引用计数 和 弱引用依赖
struct SideTable {
spinlock_t slock;// 保证操作线程安全的自旋锁;
RefcountMap refcnts;//引用计数的Map表 key-value:当isa中extra_rc不足以保存时,使用散列表保存refcnts.find(obj)
weak_table_t weak_table; //弱引用表
}
自旋锁spinlock_t
:如果把所有的类都存储在同一个表中,改动任何一个类都需要对整个表上锁,这导致操作效率和查询效率都很低。因此使用 Hash
数组 SideTables
管理多个SideTable
,每个SideTable
存储一部分对象,操作单个SideTable
并不会影响到其它的表!
1、弱引用表 weak_table_t
在ARC模式中使用关键字weak
来处理对象之间的互相强引用导致的内存泄漏问题,主要是因为使用weak
修饰变量不会导致引用计数的增加、不会影响对象的生命周期、而且在对象释放之后会将自动指针置空避免也指针访问问题!那么系统是如何管理weak
变量呢?
散列表SideTable
中的 weak_table_t
被设计用来管理指向对象的弱引用指针:
/** 全局的弱引用表是一个由自旋锁控制的哈希表 : 程序中所有的弱引用都在该表中进行存储;
* 该表以键值对的形式存储,对象作为 key,value 是结构 weak_entry_t
*/
struct weak_table_t {
weak_entry_t *weak_entries;//存储所有指向某个对象的weak指针
size_t num_entries;//哈希表的容量大小
uintptr_t mask;//参与判断引用计数辅助量
uintptr_t max_hash_displacement;//hash key 最大偏移值 : hash冲撞时最大尝试次数,用于优化搜索算法
};
哈希表weak_table_t
并不直接管理弱引用指针,而是通过 weak_entry_t
来管理!在weak_table_t
表中,每个对象对应一个 weak_entry_t
,而每个 weak_entry_t
中存储着指向该对象的所有弱引用指针。
/** 用来存储某一对象的所有弱引用指针
* @param referent 被弱引用指针指向的对象,
* @note 该结构使用两种存储方案存储弱引用指针:
* case_1:弱引用指针数量 < 4 : 使用静态数组 inline_referrers 进行保存,同时 out_of_line_ness = 0;
* case_2:弱引用指针数量 > 4 : 使用二维数组 referrers 保存, out_of_line_ness = 2;
**/
struct weak_entry_t {
DisguisedPtr<objc_object> referent;
union {
struct {//当弱引用指针个数大于 WEAK_INLINE_COUNT 时,使用二维指针数组进行存储
weak_referrer_t *referrers;
uintptr_t out_of_line_ness : 2;
uintptr_t num_refs : PTR_MINUS_2;
uintptr_t mask;
uintptr_t max_hash_displacement;
};
struct {//当弱引用指针个数小于 WEAK_INLINE_COUNT 时,使用一维数组进行存储
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
};
};
bool out_of_line() {//判断当前是否是离线存储
return (out_of_line_ness == REFERRERS_OUT_OF_LINE);
}
weak_entry_t& operator=(const weak_entry_t& other) {//重载运算符=
memcpy(this, &other, sizeof(other));
return *this;
}
//第一个弱引用指针使用该方法存储
weak_entry_t(objc_object *newReferent, objc_object **newReferrer) : referent(newReferent){
inline_referrers[0] = newReferrer;
for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
inline_referrers[i] = nil;
}
}
};
1.1、弱引用表weak_table_t
的管理
1.1.1、弱引用表的插入、删除、查询
通过插入、删除、查询操作,可以管理弱引用表weak_table_t
的某个对象 weak_entry_t
/** 将 new_entry 添加到弱引用表中
* @note 不会检查引用是否已经在表中
*/
static void weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry);
/** 从弱引用表移除弱引用 weak_entry_t
*/
static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry);
/** 在弱引用表查询指定对象的 weak_entry_t
* @param referent 指定对象,不能为 nil
* @return 如果弱引用表没有该对象的 weak_entry_t ,则返回 NULL
*/
static weak_entry_t *weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent);
1.1.2、弱引用表容量的扩增与缩减
当 weak_table_t
容量过满或者过空时,需要及时调整其大小,以优化内存的使用率,提高运行效率!
/* 调整弱引用表中的大小
* @param new_size 新的大小
*/
static void weak_resize(weak_table_t *weak_table, size_t new_size){
size_t old_size = TABLE_SIZE(weak_table);
weak_entry_t *old_entries = weak_table->weak_entries;
weak_entry_t *new_entries = (weak_entry_t *)calloc(new_size, sizeof(weak_entry_t));//申请相应大小的内存
weak_table->mask = new_size - 1;//记录weak_table实际占用的内存边界
weak_table->weak_entries = new_entries;
/* 哈希表可能会有 hash 碰撞,而 weak_table_t 使用了开放寻址法来处理碰撞。
* 如果发生碰撞的话,将寻找相邻(如果已经到最尾端的话,则从头开始)的下一个空位。
* max_hash_displacement 记录当前 weak_table 最大的偏移值,即hash函数计算的位置和实际存储位置的最大偏差。
*/
weak_table->max_hash_displacement = 0;
weak_table->num_entries = 0;// restored by weak_entry_insert below
if (old_entries) {
weak_entry_t *entry;
weak_entry_t *end = old_entries + old_size;
for (entry = old_entries; entry < end; entry++) {
if (entry->referent) {
weak_entry_insert(weak_table, entry);
}
}
free(old_entries);
}
}
/** 扩充 weak_table_t 的空间,扩充条件是表的 3/4及以上的空间已经被使用。
* @note 该表初始化大小是 64 个 weak_entry_t 的空间,每次扩充后的空间都是当前空间的两倍,即2的N次方(N>=6)
*/
static void weak_grow_maybe(weak_table_t *weak_table){
size_t old_size = TABLE_SIZE(weak_table);
if (weak_table->num_entries >= old_size * 3 / 4) {
weak_resize(weak_table, old_size ? old_size*2 : 64);
}
}
/** 缩小 weak_table_t 的空间,缩小的条件是表的容量不小于1024个weak_entry_t的空间,并且低于1/16的空间被占用
* 缩小后的空间是当前空间的1/8。
*/
static void weak_compact_maybe(weak_table_t *weak_table){
size_t old_size = TABLE_SIZE(weak_table);
if (old_size >= 1024 && old_size / 16 >= weak_table->num_entries) {
weak_resize(weak_table, old_size / 8);
}
}
1.2、weak_entry_t
的插入与删除
weak_entry_t
使用数组存储指向某个对象的所有弱引用指针!Runtime 提供了一些函数可以将弱引用指针插入这个数组、从数组移除指定的弱引用指针、扩充数组容量等操作
/** 将弱引用指针存储至weak_entry_t的数组中
* 该函数主要做了以下几件事:
* 1、当前没有使用离线存储,将 new_referrer 保存至数组 inline_referrers的空余位置;
* 如果没有空余位置,将数组 new_referrer 的元素拷至数组 referrers 并设置为离线存储;
* 2、如果数组 new_referrer 当前已经使用了总量的3/4 ,则需要扩充容量,并将指针 new_referrer 存储至 weak_entry_t
* 3、如果数组 new_referrer 当前还未超出总量的3/4 ,则直接将指针 new_referrer 存储至 weak_entry_t
*/
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
/** 从指定 weak_entry_t 实例中的数组移出某个弱引用指针
* 该函数主要做了以下几件事:
* 1、如果使用非离线存储,则遍历静态数组 inline_referrers,找到 old_referrer 则移出数组,否则继续执行;
* 2、如果使用离线存储:使用 w_hash_pointer(old_referrer) & (entry->mask)获取起始索引,
* 遍历entry->referrers,找到之后置空,并entry->num_refs自减.
*/
static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer)
/** 扩容并增加新的弱引用指针,最终的存储还是依靠append_referrer() 函数完成
*/
static void grow_refs_and_insert(weak_entry_t *entry,objc_object **new_referrer)
1.3、weak
变量的管理机制
疑问:弱引用指针是如何一步步的存储到weak_entry_t
的数组中,被weak_table_t
管理的呢?
弱引用指针的使用有多种情况:
- 1、初始化一个弱引用指针并赋值:
__weak MyModel *weakModel = model;
- 2、将一个已声明的弱引用指针重新赋值:
*weakModel = model;
- 3、被弱引用的对象释放
dealloc
;
我们分别看下它们的底层调用有何不同!
1.3.1、注册一个有效的弱引用指针
注册一个有效的弱引用指针,底层函数调用流程:
- __weak MyModel *weakModel = model;
└── id objc_initWeak(id *location, id newObj)
└─ id storeWeak(id *location, objc_object *newObj)
└─ id weak_register_no_lock()
└─ void append_referrer()
├─ void weak_grow_maybe()
├─ void weak_entry_insert()
I、 初始化一个新的弱引用指针
/** 初始化一个新的弱引用指针指向对象的地址
* @param location 弱引用指针的内存地址
* @param newObj 弱指针指向的新对象
* @note 该函数不是线程安全的
* @note 函数有一个前提条件:就是object必须是一个没有被注册为__weak对象的有效指针
*/
id objc_initWeak(id *location, id newObj){
if (!newObj) {//判断原始引用对象是否为空
*location = nil;
return nil;
}
//调用 objc_storeWeak() 函数,更新指针指向,创建对应的弱引用表
return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>(location, (objc_object*)newObj);
}
II、新指针指向,创建对应的弱引用表
/** 更新指针指向,创建对应的弱引用表
* 该函数主要做了以下几件事:
* 1、分别获取新、旧值相关联的 SideTable
* 2、如果有旧值就调用 weak_unregister_no_lock() 函数,从旧值的 weak_entry_t 数组中移出旧指针
* 3、如果有新值就调用 weak_register_no_lock() 函数分配新值,并标记新值存在弱引用
*/
template <HaveOld haveOld, HaveNew haveNew,CrashIfDeallocating crashIfDeallocating>
static id storeWeak(id *location, objc_object *newObj){
assert(haveOld || haveNew);//断言新值和旧值至少有一个是存在
if (!haveNew) assert(newObj == nil);
//在类没有完成 +initialized 方法之前调用 weakStore 时,作为初始化的标识
Class previouslyInitializedClass = nil;
id oldObj;
SideTable *oldTable;
SideTable *newTable;
retry:
/******************* 分别获取新旧值相关联的引用表 *****************/
if (haveOld) {
oldObj = *location;//获取弱引用指针的旧指向
oldTable = &SideTables()[oldObj];//获取oldObj对应的弱引用表
} else {
oldTable = nil;
}
if (haveNew) {
newTable = &SideTables()[newObj];//获取newObj对应的弱引用表
} else {
newTable = nil;
}
SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);//上锁
if (haveOld && *location != oldObj) {//如果旧值改变就重新获取旧值相关联的表
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
goto retry;
}
// 如果有新值,判断新值所属的类是否已经初始化,如果没有初始化,则先执行初始化,防止+initialize内部调用 storeWeak() 产生死锁
if (haveNew && newObj) {
Class cls = newObj->getIsa();
if (cls != previouslyInitializedClass && !((objc_class *)cls)->isInitialized()){
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
_class_initialize(_class_getNonMetaClass(cls, (id)newObj));
// 如果这个类在它自身的一个实例上调用storeWeak()进行 +initialize,那么我们可以继续,但是它将显示为正在初始化,并且还没有初始化到上面的检查中。
previouslyInitializedClass = cls;
goto retry;
}
}
/*********** 如果旧值存在,则从旧值的 weak_entry_t 数组中移出旧指针 **********/
if (haveOld) {
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
/*********** 如果有新值,绑定新值与弱引用指针 **********/
if (haveNew) {
newObj = (objc_object *)weak_register_no_lock(&newTable->weak_table, (id)newObj, location,crashIfDeallocating);
if (newObj && !newObj->isTaggedPointer()) {
newObj->setWeaklyReferenced_nolock();//标记新值存在弱引用
}
*location = (id)newObj;//不要在其他地方设置 *location。那会引起数据竞争。
}else {
// 没有新值,存储没有更改。
}
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
return (id)newObj;
}
注意: 使用模板参数进行常量参数传递是为了优化性能,预判了大概率会发生的事情优先处理;
- param
HaveOld
是否有旧的引用,如果有旧的引用则需要释放; - param
HaveNew
是否有新的引用,如果有则需要存储新的引用; - param
crashifdeallocate
为true
,则当newObj
正在释放或newObj
的类不支持弱引用时,进程将停止;为false
则存储nil
。 - case1 初始化一个弱引用指针并赋值,该指针没有旧值:
HaveOld=false,haveNew=true
; - case2 将弱引用指针指向
nil
,该指针没有新值:HaveOld=true,haveNew=false
; - case3 将一个指向其它对象的弱引用指针重新赋值:
HaveOld=true,haveNew=true
;
III、向弱引用表里添加新的弱应用指针
/** 向弱引用表里添加新的弱应用指针
* 该函数主要做了以下几个事情:
* 1、如果被引用对象正在释放,则不能再添加弱应用指针
* 2、在弱引用表查询指定对象的 weak_entry_t
* 2.1、如果查到 weak_entry_t ,则将弱引用指针存储至 weak_entry_t
* 2.2、如果没有查到 weak_entry_t,新建一个 weak_entry_t 并存储弱指针,将 weak_entry_t 存储在弱引用表
* @param referent 被引用对象,不能为空、不能是TaggedPointer
* @note 在 -dealloc 方法中,不能增加新的弱引用指针,否则会报错.
*/
id weak_register_no_lock(weak_table_t *weak_table, id referent_id, id *referrer_id, bool crashIfDeallocating){
objc_object *referent = (objc_object *)referent_id;//获取被引用对象
objc_object **referrer = (objc_object **)referrer_id;//强转referrer_id为二级指针
if (!referent||referent->isTaggedPointer()) return referent_id;
/*********************** 如果被引用对象正在释放,则不能再添加弱应用指针 ***********************/
bool deallocating;//确保被引用对象是可行的
if (!referent->ISA()->hasCustomRR()) {
deallocating = referent->rootIsDeallocating();
}else {
BOOL (*allowsWeakReference)(objc_object *, SEL) = (BOOL(*)(objc_object *, SEL))
object_getMethodImplementation((id)referent, SEL_allowsWeakReference);
if ((IMP)allowsWeakReference == _objc_msgForward) {
return nil;
}
deallocating = !(*allowsWeakReference)(referent, SEL_allowsWeakReference);
}
if (deallocating) {//如果对象正在释放,不能增加新的弱引用指针
if (crashIfDeallocating) {
_objc_fatal("Cannot form weak reference to instance (%p) of "
"class %s. It is possible that this object was "
"over-released, or is in the process of deallocation.",
(void*)referent, object_getClassName((id)referent));
} else {
return nil;
}
}
/*********************** 在弱引用表查询指定对象的 weak_entry_t ***********************/
weak_entry_t *entry;
if ((entry = weak_entry_for_referent(weak_table, referent))) {
/********* 将弱引用指针referrer存储到对象referent关联的weak_entry_t中 *************/
append_referrer(entry, referrer);
} else {//在weak_table中未找到referent对应的weak_entry_t
/********* 新建一个 weak_entry_t 并存储弱指针,将 weak_entry_t 存储在弱引用表 *************/
weak_entry_t new_entry(referent, referrer);//给对象referent创建一个新的引用列表
weak_grow_maybe(weak_table);// weak_table 增加内存
weak_entry_insert(weak_table, &new_entry);//把referent的引用列表加入到weak_table中
}
// Do not set *referrer.
// objc_storeWeak() requires that the value not change.
return referent_id;
}
IV、将弱引用指针存储至weak_entry_t
的数组中
/** 将弱引用指针存储至weak_entry_t的数组中
* 该函数主要做了以下几件事:
* 1、当前没有使用离线存储,将 new_referrer 保存至数组 inline_referrers的空余位置;
* 如果没有空余位置,将数组 new_referrer 的元素拷至数组 referrers 并设置为离线存储;
* 2、如果数组 new_referrer 当前已经使用了总量的3/4 ,则需要扩充容量,并将指针 new_referrer 存储至 weak_entry_t
* 3、如果数组 new_referrer 当前还未超出总量的3/4 ,则直接将指针 new_referrer 存储至 weak_entry_t
*/
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer){
//在增加新的弱引用指针之前使用非离线存储弱引用指针:使用静态数组inline_referrers来进行存储
if (!entry->out_of_line()) {
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {//遍历数组inline_referrers,是否存在空位置
if (entry->inline_referrers[i] == nil) {//将新的弱引用指针存储在该数组空位置上,并返回
entry->inline_referrers[i] = new_referrer;
return;
}
}
// 在静态数组中没有可用的存储位置,需要开辟离线空间
weak_referrer_t *new_referrers = (weak_referrer_t *)calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t));
//将之前inline_referrers数组的元素复制到数组 new_referrers 中
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
new_referrers[i] = entry->inline_referrers[i];
}
entry->referrers = new_referrers;
entry->num_refs = WEAK_INLINE_COUNT;
entry->out_of_line_ness = REFERRERS_OUT_OF_LINE;
entry->mask = WEAK_INLINE_COUNT-1;
entry->max_hash_displacement = 0;
}
assert(entry->out_of_line());//断言:代码执行到这个位置时 entry 应该是离线存储
if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {//如果当前已经使用了总量的3/4,则扩容并添加新的引用,并返回
return grow_refs_and_insert(entry, new_referrer);
}
//如果当前已经使用量小于总量的3/4,则直接添加
size_t begin = w_hash_pointer(new_referrer) & (entry->mask);
size_t index = begin;
size_t hash_displacement = 0;
while (entry->referrers[index] != nil) {//发生hash碰撞
hash_displacement++;
index = (index+1) & entry->mask;
if (index == begin) bad_weak_table(entry);
}
//更新存储时最大的hash碰撞次数,由于优化取值时算法减少搜索次数
if (hash_displacement > entry->max_hash_displacement) {
entry->max_hash_displacement = hash_displacement;
}
weak_referrer_t &ref = entry->referrers[index];
ref = new_referrer;
entry->num_refs++;
}
至此,Runtime 已经初始化一个新的弱引用指针,并将它存储在散列表 SideTable
!
1.3.2、将被注册的弱引用指针重新赋值
- weakModel = model;
└── id objc_storeWeak(id *location, id newObj)
└─ id storeWeak(id *location, objc_object *newObj)
└─ void weak_unregister_no_lock()
└─ void remove_referrer()
└─void void weak_entry_remove()
└─ id weak_register_no_lock()
└─ void append_referrer()
├─ void weak_grow_maybe()
├─ void weak_entry_insert()
将已经被注册的弱引用指针再次指向别的变量,会通过objc_storeWeak()
函数调用storeWeak()
函数!在storeWeak()
函数中,首先需要将被注册的弱引用指针从被引用变量weak_entry_t
的数组中移除;然后才可以将该弱引用指针与新变量重新关联!
/** 移除指定的弱引用指针:
* 该函数主要做了以下几件事:
* 1、在 weak_table 中查找对应的 weak_entry_t;
* 2、从 weak_entry_t 的数组中移除referrer指针;
* 3、判断 weak_entry_t 的数组是否为空,若不再存储弱引用指针,则从弱引用表中移除 weak_entry_t;
*/
void weak_unregister_no_lock(weak_table_t *weak_table, id referent_id,id *referrer_id){
objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;
weak_entry_t *entry;
if (!referent) return;
/**************** 在弱引用表查询指定对象的 weak_entry_t *************/
if ((entry = weak_entry_for_referent(weak_table, referent))) {
/********** 从指定 weak_entry_t 实例中的数组移出某个弱引用指针 **********/
remove_referrer(entry, referrer);
/********** 若 weak_entry_t 的数组为空,则从弱引用表中移除 weak_entry_t **********/
bool empty = true;//判断移除指针之后entry数组是否为空
if (entry->out_of_line() && entry->num_refs != 0) {
empty = false;
}else {
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i]) {
empty = false;
break;
}
}
}
if (empty) {//如果移出指针之后entry数组为空,则从弱引用表中移除entry
weak_entry_remove(weak_table, entry);
}
}
}
1.3.3、弱引用变量 dealloc
当对象调用 -dealloc
方法时,Runtime 会从弱引用表清空指向该对象的所有弱引用指针:
- [weakModel dealloc];
└── [NSObject dealloc];
└─ void _objc_rootDealloc(id obj)
└─ void objc_object::rootDealloc()
└─ id object_dispose(id obj)
└─ void *objc_destructInstance(id obj)
└─ void objc_object::clearDeallocating()
└─ void objc_object::clearDeallocating_slow()
└─ void weak_clear_no_lock()
关键的函数是 weak_clear_no_lock()
/** 当对象调用 -dealloc 方法时,从弱引用表清空某个对象的所有弱引用指针
*/
void weak_clear_no_lock(weak_table_t *weak_table, id referent_id){
objc_object *referent = (objc_object *)referent_id;
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
if (entry == nil) {//若entry为空则证明当前对象不存在弱引用指针.
return;
}
weak_referrer_t *referrers;//声明一个数组用来存储所有指向该对象的弱引用指针
size_t count;//数组容量
if (entry->out_of_line()) {//使用离线存储弱引用指针
referrers = entry->referrers;
count = TABLE_SIZE(entry);
} else { //使用内部静态数组存储弱引用指针
referrers = entry->inline_referrers;
count = WEAK_INLINE_COUNT;
}
//遍历数组,将所有指向该对象的弱引用指针全部指向 nil
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
if (referrer) {
if (*referrer == referent) {//当前弱指针指向对象referent
*referrer = nil;//置空弱引用指针
}else if (*referrer) {
_objc_inform("__weak variable at %p holds %p instead of %p. "
"This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
referrer, (void*)*referrer, (void*)referent);
objc_weak_error();
}
}
}
weak_entry_remove(weak_table, entry); //从weak_table中移除entry
}
2、强引用表 RefcountMap
2.1、+alloc
方法
- [MyModel alloc];
└── id objc_alloc(Class cls)
└─ id callAlloc()
└─ [NSObject alloc];
└─ id _objc_rootAlloc(Class cls)
└─ id callAlloc()
└─ id class_createInstance()
└─ id _class_createInstanceFromZone()
2.2、-retain
方法
/** 对象的引用计数 +1
* 该函数主要做了以下几件事:
* 1、通过对象内存地址,在SideTables找到对应的SideTable
* 2、通过对象内存地址,在refcnts中取出引用计数
* 3、判断引用计数是否增加到最大值,如果没有,则 +4
*/
id objc_object::sidetable_retain(){
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
/****************** 在SideTables找到对应的SideTable ******************/
SideTable& table = SideTables()[this];
table.lock();
/****************** 在 RefcountMap 中取出引用计数 ******************/
size_t& refcntStorage = table.refcnts[this];
if (!(refcntStorage & SIDE_TABLE_RC_PINNED)) {
/****************** 没有到最大值,1 则+4 ******************/
refcntStorage += SIDE_TABLE_RC_ONE;
}
table.unlock();
return (id)this;
}
2.3、-release
方法
/** 对象的引用计数 -1
* 该函数主要做了以下几件事:
* 1、通过对象内存地址,在 SideTables 找到对应的SideTable
* 2、通过对象内存地址,在refcnts中取出引用计数
* 3、根据当前引用计数分别做出对应处理:
* 3.1、没有引用计数,标记为正在释放状态
* 3.2、强引用计数为 0 ,不做操作
* 3.3、没有达到最高位,则引用计数 -1
* 4、如果需要释放,则调用 -dealloc 方法
*/
uintptr_t objc_object::sidetable_release(bool performDealloc){
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
/****************** 在 SideTables 找到对应的SideTable ******************/
SideTable& table = SideTables()[this];
bool do_dealloc = false;
table.lock();
/****************** 在 RefcountMap 中取出引用计数 ******************/
RefcountMap::iterator it = table.refcnts.find(this);
if (it == table.refcnts.end()) {
/* table.refcnts.end()表示使用一个iterator迭代器到达了end()状态
* end() 状态表示从头开始查找,一直找到最后都没有找到
* 该条 if 语句表示查找到最后都没找到引用计数表 RefcountMap
*/
do_dealloc = true;
table.refcnts[this] = SIDE_TABLE_DEALLOCATING;//标记对象为正在释放
} else if (it->second < SIDE_TABLE_DEALLOCATING) {
//高位的引用计数位都是0,低位的弱引用标记位可能有弱引用为 1、也可能没有弱引用为 0
do_dealloc = true;
it->second |= SIDE_TABLE_DEALLOCATING; //不会影响 弱引用标记位
} else if (!(it->second & SIDE_TABLE_RC_PINNED)) {
it->second -= SIDE_TABLE_RC_ONE; //引用计数 -1
}
table.unlock();
if (do_dealloc && performDealloc) {//如果需要释放对象,则调用dealloc
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return do_dealloc;
}