读weak之前先了解三个数据结构:SideTable、weak_table_t、weak_entry_t
一、基本数据结构
1.SideTable结构体
管理着引用计数表与弱引用表
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;//引用计数表
weak_table_t weak_table;//弱引用表
...
};
2.weak_table_t结构体
全局弱引用表。将对象id存储为键,和weak_entry_t结构作为它们的值。
struct weak_table_t {
weak_entry_t *weak_entries;// hash数组,用来存储弱引用对象的相关信息weak_entry_t
size_t num_entries;// hash数组中的元素个数
uintptr_t mask;// hash数组长度-1,会参与hash计算。(注意,这里是hash数组的长度,而不是元素个数。比如,数组长度可能是64,而元素个数仅存了2个)
uintptr_t max_hash_displacement;// 可能会发生的hash冲突的最大次数
};
3. weak_entry_t
静态数组与动态数组结合的结构体,数量小于等于4存静态数组中,大于4存动态数组
struct weak_entry_t {
DisguisedPtr<objc_object> referent;// 被弱引用的对象
// 引用该对象的对象列表,联合。 引用个数小于4,用inline_referrers数组。 用个数大于4,用动态数组weak_referrer_t *referrers
union {
struct {
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;// 最大的hash冲突次数(说明了最多做max_hash_displacement次hash冲突,肯定会找到对应的数据)
};
struct {
// out_of_line_ness field is low bits of inline_referrers[1]
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
};
};
...
};
二、初始化
1.入口
//weak源码分析
void weak(){
Person * person = [[Person alloc] init];
//指针地址指向指针
__weak Person *weakPerson = person;
__weak Person *weakPerson2 = person;
NSLog(@"person指针:%p\nweakPerson指针:%p",person,weakPerson);
NSLog(@"person指针地址:%p\nweakPerson指针地址:%p",&person,&weakPerson);
}
流程:objc_initWeak(id *location, id newObj)->storeWeak(id *location, objc_object *newObj)->weak_register_no_lock(...)
2.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;//指针地址
// 如果referent为nil 或 referent 采用了TaggedPointer计数方式,直接返回,不做任何操作
if (!referent || referent->isTaggedPointer()) return referent_id;
// ensure that the referenced object is viable
// 确保被引用的对象可用(没有在析构,同时应该支持weak引用)
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_table中找到referent对应的weak_entry,并将referrer加入到weak_entry中
weak_entry_t *entry;
if ((entry = weak_entry_for_referent(weak_table, referent))) {// 如果能找到weak_entry,则讲referrer插入到weak_entry中
append_referrer(entry, referrer);// 将referrer追加到weak_entry_t的引用数组中
}
else {
weak_entry_t new_entry(referent, referrer);//创建weak_entry_t
weak_grow_maybe(weak_table);//扩大数组操作
weak_entry_insert(weak_table, &new_entry);//new_entry插入weak_table
}
return referent_id;
}
- 对象指针不能为空,非TaggedPointer对象。
- 判断对象没有析构
- 最主要是最后面一段:
1.分配一个新的new_entry对象
2.weak_grow_maybe判断weak_table是否需要扩容
3.new_entry插入到weak_table中
4.如果weak_table中有referent,用append_referrer追加到entry中。
3.weak_entry_for_referent(weak_table, referent)
查找weak_table是否包含了referent。一段while循环查找。
static weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
assert(referent);
weak_entry_t *weak_entries = weak_table->weak_entries;
if (!weak_entries) return nil;
size_t begin = hash_pointer(referent) & weak_table->mask;
size_t index = begin;
size_t hash_displacement = 0;
while (weak_table->weak_entries[index].referent != referent) {
index = (index+1) & weak_table->mask;
if (index == begin) bad_weak_table(weak_table->weak_entries);
hash_displacement++;
if (hash_displacement > weak_table->max_hash_displacement) {
return nil;
}
}
return &weak_table->weak_entries[index];
}
4.weak_grow_maybe
判断weak_table是否需要扩容操作,当实际个数>=总长度的3/4时,在原有基础上扩大2倍。
static void weak_grow_maybe(weak_table_t *weak_table)
{
size_t old_size = TABLE_SIZE(weak_table);
// Grow if at least 3/4 full.
if (weak_table->num_entries >= old_size * 3 / 4) {// 当大于现有长度的3/4时,会做数组扩容操作。
weak_resize(weak_table, old_size ? old_size*2 : 64);// 初次会分配64个位置,之后在原有基础上*2
}
}
5.weak_entry_insert把new_entry插入到weak_table_t中。
三.销毁
流程:objc_destroyWeak->storeWeak->weak_unregister_no_lock->remove_referrer
1.weak_unregister_no_lock
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;
// 如果referent为nil 或 referent 采用了TaggedPointer计数方式,直接返回,不做任何操作
if (!referent) return;
if ((entry = weak_entry_for_referent(weak_table, referent))) {// 查找
remove_referrer(entry, referrer);// 在referent所对应的weak_entry_t的hash数组中,移除referrer
// 移除元素之后, 要检查一下weak_entry_t的hash数组是否已经空了
bool empty = true;
//使用了可变数组,且数量不为0
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_t的hash数组已经空了,则需要将weak_entry_t从weak_table中移除
weak_entry_remove(weak_table, entry);
}
}
}
- weak_entry_for_referent在weak_table查找referent,entry不为空做移除
- remove_referrer从weak_table移除referrer
- out_of_line动态数组标识与静态数组如果还不为空,weak_entry_remove移除entry
2.remove_referrer
static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer)
{
if (! entry->out_of_line()) {//静态数组
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i] == old_referrer) {
entry->inline_referrers[i] = nil;
return;
}
}
_objc_inform("Attempted to unregister unknown __weak variable "
"at %p. This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
old_referrer);
objc_weak_error();
return;
}
//动态数组
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_inform("Attempted to unregister unknown __weak variable "
"at %p. This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
old_referrer);
objc_weak_error();
return;
}
}
entry->referrers[index] = nil;
entry->num_refs--;
}
如果是静态数组,置空后直接return.否则动态数组释放
3.weak_entry_remove
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));//清理entry sizeof(*entry)个字节
weak_table->num_entries--;
weak_compact_maybe(weak_table);//收容
}
释放,收容。
4.释放
局部变量在方法结束后立即释放,全局变量会在dealloc方法是否。
总结:
- weak的使用静态与动态数组提高了效率
- 动态数组,实质在插入数据时判断实际个数与总个数的大小,判断是否需要扩容。释放时也是如此来达到收容。
- 对于dealloc中的释放,会根据弱引用标志位weakly_referenced调用weak_clear_no_lock进行释放。