weak总结
系统创建了一个全局的哈希表SideTables,它使用对象的内存地址作key,值为SideTable结构体。SideTable维护对象的引用计数器和弱引用表。弱引用表weak_table也是个哈希表,key是weak指针指向对象的地址,value是weak_entry_t对象,weak_entry_t存储着所有指向该对象的weak指针地址数组。
weak 的实现原理可以概括如下:
- 当有weak指针修饰对象,runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象。
- objc_initWeak函数内部会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新或创建对应的弱引用表。
- 对象释放时,会调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。
源码中对象的定义如下:
struct SideTable {
spinlock_t slock;
RefcountMap refcnts; // 计数器表
weak_table_t weak_table; // 弱引用表
}
struct weak_table_t {
// 弱引用散列表,key是对象地址,value是weak_entry_t对象
weak_entry_t *weak_entries;
size_t num_entries;
uintptr_t mask;
uintptr_t max_hash_displacement;
};
struct weak_entry_t {
DisguisedPtr<objc_object> referent;//the address of a __weak variable.
union {
struct {
// 某个对象全部的弱引用指针地址
weak_referrer_t *referrers;
//最低有效位,也是标志位。当标志位 0 时,增加引用表指针纬度。
uintptr_t out_of_line_ness : 2;
uintptr_t num_refs : PTR_MINUS_2;//引用数值。
uintptr_t mask;//计数辅助量
uintptr_t max_hash_displacement;//hash 元素上限阀值
};
struct {
// out_of_line_ness field is low bits of inline_referrers[1]
//weak指针数量小于等于4时,存储在inline_referrers中,超过4个才会存储到referrers中
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
};
};
}
下面是具体函数的实现:
id objc_initWeak(id *location, id newObj)
{
if (!newObj) {
*location = nil;
return nil;
}
return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
objc_initWeak函数只是做个非空判断,接着调用objc_storeWeak函数:
//如果HaveOld为真,变量有值需要被清空。值可能为空。
//如果HaveNew为真,需要为变量分配一个新值,值可能为空。
//如果CrashIfDeallocating为真,且newObj正在释放或newObj的类不支持弱引用,这个过程停止。如果CrashIfDeallocating为false,存储nil。
enum CrashIfDeallocating {
DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
};
template <HaveOld haveOld, HaveNew haveNew,
CrashIfDeallocating crashIfDeallocating>
static id
storeWeak(id *location, objc_object *newObj)
{
assert(haveOld || haveNew);
if (!haveNew) assert(newObj == nil);
Class previouslyInitializedClass = nil;
id oldObj;
SideTable *oldTable;
SideTable *newTable;
retry:
if (haveOld) {
oldObj = *location;//根据weak指针获取对象地址
oldTable = &SideTables()[oldObj];//根据对象获得对应SideTable
} else {
oldTable = nil;
}
if (haveNew) {
newTable = &SideTables()[newObj];//根据对象获得对应SideTable
} else {
newTable = nil;
}
//加锁
SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
if (haveOld && *location != oldObj) {
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
goto retry;
}
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));
previouslyInitializedClass = cls;
goto retry;
}
}
// Clean up old value, if any.清除旧值
if (haveOld) {
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
// Assign new value, if any.分配新值
if (haveNew) {
newObj = (objc_object *)
weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
crashIfDeallocating);
if (newObj && !newObj->isTaggedPointer()) {
newObj->setWeaklyReferenced_nolock();
}
// 之前不要设置 location 对象,这里需要更改指针指向
*location = (id)newObj;
}
else {
// No new value. The storage is not changed.
}
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
return (id)newObj;
}
清除旧值源码如下,具体清除的步骤在remove_referrer函数中:
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))) {
remove_referrer(entry, referrer); //将entry中的弱引用置为nil
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;
}
}
}
if (empty) {
weak_entry_remove(weak_table, entry);
}
}
// Do not set *referrer = nil. objc_storeWeak() requires that the
// value not change.
}
static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer)
{
if (! entry->out_of_line()) {
//如果弱引用指针小于等于4,将inline_referrers中的weak指针全部置为nil
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i] == old_referrer) {
entry->inline_referrers[i] = nil;
return;
}
}
......
objc_weak_error();
return;
}
// 根据对象获取对应weak_entry_t的位置
size_t begin = w_hash_pointer(old_referrer) & (entry->mask);
size_t index = begin;
size_t hash_displacement = 0;
while (entry->referrers[index] != old_referrer) {
index = (index+1) & entry->mask;
if (index == begin) bad_weak_table(entry);
hash_displacement++;
if (hash_displacement > entry->max_hash_displacement) {
......
objc_weak_error();
return;
}
}
//如果弱引用指针大于4,只将referrers中对象对应的weak指针置为nil
entry->referrers[index] = nil;
entry->num_refs--;
}
static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry)
{
// remove entry
if (entry->out_of_line()) free(entry->referrers);
bzero(entry, sizeof(*entry));
weak_table->num_entries--;
weak_compact_maybe(weak_table);
}
源码中remove_referrer函数是将inline_referrers中的所有指针置为nil或者referrers中old_referrer对应位置的weak指针置为nil,所以我推测,当对象的某个weak指针销毁了或者weak指针数量超过4个的情况,storeWeak函数的传参HaveOld才为true,如果inline_referrers有值表示weak指针数量要超过4个,则将inline_referrers内的weak指针置为nil并返回。否则去清除inline_referrers中对应的weak指针。
然后我们看新增的weak指针是如何添加的,weak_register_no_lock的源码如下:
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;
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);
}
......
// now remember it and where it is being stored
//添加的步骤在这里
weak_entry_t *entry;
if ((entry = weak_entry_for_referent(weak_table, referent))) {
//如果原来就存在对象对应的entry,说明之前添加过weak指针,直接往后拼接
//(referent是weak修饰的对象, referrer是weak指针,referent、referrer 傻傻分不清楚哈哈)
append_referrer(entry, referrer);
}
else {
//原本不存在对象对应的entry,则新建一个new_entry
weak_entry_t new_entry(referent, referrer);
weak_grow_maybe(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_table中获取weak_entry_t,如果存在,将新增的weak指针追加到weak_entry_t 的inline_referrers或者referrers中(可查看函数append_referrer具体实现),如果不存在,则以对象和weak指针为参数新建一个weak_entry_t,并将这个weak_entry_t插入到weak_table中。
当一个对象释放,会自动调用dealloc方法,具体的调用流程如下:
- dealloc
- _objc_rootDealloc
- rootDealloc
- object_dispose
- objc_destructInstance
- clearDeallocating
- sidetable_clearDeallocating\clearDeallocating_slow
- weak_clear_no_lock
weak_clear_no_lock的源码如下
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) {
/// XXX shouldn't happen, but does with mismatched CF/objc
//printf("XXX no entry for clear deallocating %p\n", referent);
return;
}
// zero out references
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;
}
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
if (referrer) {
if (*referrer == referent) {
*referrer = nil;//循环将weak指针地址置为nil
}
else if (*referrer) {
......
objc_weak_error();
}
}
}
weak_entry_remove(weak_table, entry);
}
先跟out_of_line的值去计算weak指针的数量count,然后循环遍历指针,如果指针是指向对应对象的话,将指针置为nil。