在日常开发过程中,经常会出现循环引用而导致的内存泄露的问题,比如我们有a,b两个对象,对象中都有两个属性name和age,然后出现了下列情况
a.name = b.name;
b.age = a.age;
- a对象通过a.name引用了b对象,所以b的引用计数为1
- b对象通过b.age引用了a对象,所以a的引用计数也为1
这时a执行完任务后,发现引用计数还是1,不能释放。b执行完任务后,发现引用计数还是1,也不能释放。
我们看到它们彼此都需要对方,然后boom!循环引用。
这时候我们可以加一个__weak修饰,
__weak a.name = b.name;
b.age = a.age;
- a对象通过__weak a.name弱引用了b对象,所以b的引用计数为0。
- b对象通过b.age引用了a对象,所以a的引用计数也为1。
这时,a执行完任务后,发现引用计数还是1,不能释放。b在执行完任务后引用计数变为0,b对象被释放,从而a的引用计数减1变成0,也被释放。
那么weak在这里面是如何执行的呢?下面开始我们的正题。

上图是weak修饰对象时系统底层的流程,下面结合runtime源码梳理一下。
1、objc_initWeak初始化
//location是弱引用对象的内存地址
id objc_initWeak(id *location, id newObj) {
if (!newObj) {
*location = nil;
return nil;
}
return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
2、调用storeWeak函数存储weak弱引用指针
直接看代码
static id
storeWeak(id *location, objc_object *newObj)
{
Class previouslyInitializedClass = nil;
id oldObj;
SideTable *oldTable;
SideTable *newTable;
//如果之前存储过这个弱引用对象
if (haveOld) {
//则把原来弱引用对象从weak_table中移除
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
//如果这是新的弱引用对象
if (haveNew) {
//把这个新的弱引用对象添加到weak_table中
newObj = (objc_object *)
weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
crashIfDeallocating);
// weak_register_no_lock returns nil if weak store should be rejected
//判断这个对象是否属于TaggedPointer类型的,如果属于TaggedPointer类型那就厉害了,根本不需要有引用计数一说
//taggedPointer对象是为了苹果为了性能最大化做的处理,针对不需要到栈和堆中寻找的对象,可以直接从地址中通过一定的算法得到他们的值。
if (newObj && !newObj->isTaggedPointer()) {
//标记该对象是弱引用对象
newObj->setWeaklyReferenced_nolock();
}
// Do not set *location anywhere else. That would introduce a race.
*location = (id)newObj;
}
else {
// No new value. The storage is not changed.
}
//给这两个表解锁
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
return (id)newObj;
}
那这里面有两个重要的函数weak_unregister_no_lock
和weak_register_no_lock
1)首先判断弱引用指针表中是否有当前要存储的weak弱引用指针,看代码
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_for_referent找到entry弱引用指针条目
if ((entry = weak_entry_for_referent(weak_table, referent))) {
//从内层inline_referrers中移除entry
//inline_referrers中只能存储4个弱引用指针,多了就要存储到referrers中,所以要多一步empty判空操作
remove_referrer(entry, referrer);
bool empty = true;
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;
}
}
}
//从weak_table中移除entry弱引用条目
if (empty) {
weak_entry_remove(weak_table, entry);
}
}
// Do not set *referrer = nil. objc_storeWeak() requires that the
// value not change.
}
2)调用weak_register_no_lock存储entry弱引用指针条目,看代码
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;//弱引用变量的地址
//如果该弱引用对象是taggedPointer对象,则不做处理直接返回该对象
//taggedPointer对象是为了苹果为了性能最大化做的处理,针对不需要到栈和堆中寻找的对象,可以直接从地址中通过一定的算法得到他们的值。
if (!referent || referent->isTaggedPointer()) return referent_id;
// ensure that the referenced object is viable
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;
}
}
// now remember it and where it is being stored
weak_entry_t *entry;//弱引用指针的条目
//判断weak_table中是否有该条目
if ((entry = weak_entry_for_referent(weak_table, referent))) {
//如果有,则把弱引用对象追加进去
append_referrer(entry, referrer);
}
else {
//如果没有,则创建一个
weak_entry_t new_entry(referent, referrer);
//如果索引已经超过原来的3/4,则给weak_table扩容
weak_grow_maybe(weak_table);
//将新的entry插入weak_table
weak_entry_insert(weak_table, &new_entry);
}
// Do not set *referrer. objc_storeWeak() requires that the
// value not change.
return referent_id;
}
综上,就是系统在使用weak的存储原理,当然也可以继续深入的探索,日后会抽时间继续探索哒,希望能和大家共同进步。
参考博客:
iOS管理对象内存的数据结构以及操作算法--SideTables、RefcountMap、weak_table_t-一
Objective-C 小记(10)__weak
深入浅出ARC(上)
网友评论