主要参考:
weak 弱引用的实现方式
详解获取weak对象的过程
解析:SideTables, SideTable, weak_table, weak_entry_t
weak修饰有什么用?
声明为weak的指针,weak指针指向的对象一旦被释放,weak的指针都将被赋值为nil
weak的赋值与访问
NSObject *obj = [[NSObject alloc] init];
// weak的三种赋值情况
// (1)变量赋值
_weakObj = obj; // 编译为:objc_storeWeak(&_weakObj, obj);
// (2) 直接初始化,strong对象赋值
__weak NSObject *obj1 = obj; // 编译为:objc_initWeak(&obj1, obj);
// (3) 直接初始化,weak对象赋值
__weak NSObject *obj2 = _weakObj; // 编译为:objc_copyWeak(&obj2, & _weakObj);
// weak的访问情况,就是调用 objc_loadWeakRetained(id *location)
NSLog(@"=====%@",_weakObj);
// 编译为下面代码
/*
id temp = objc_loadWeakRetained(&weakObj);
NSLog(@"=====%@",temp);
objc_release(temp);
*/
weak流程图
NSObject.mm里面的代码
objc_initWeak:定义并赋值一个weak变量
id
objc_initWeak(id *location, id newObj)
{
// 相比objc_storeWeak就是 多了这个判断
if (!newObj) {
*location = nil;
return nil;
}
// 这里传递了三个 bool 数值
// 使用 template 进行常量参数传递是为了优化性能
// DontHaveOld--没有旧对象,
// DoHaveNew--有新对象,
// DoCrashIfDeallocating-- 如果释放了就Crash提示
return storeWeak
(location, (objc_object*)newObj);
}
objc_storeWeak:用strong对象赋值
id
objc_storeWeak(id *location, id newObj)
{
return storeWeak
(location, (objc_object *)newObj);
}
objc_copyWeak:用weak对象赋值
void
objc_copyWeak(id *dst, id *src)
{
// 函数取出附有__weak修饰符变量所引用的对象并retain
id obj = objc_loadWeakRetained(src);// 根据scr获取指向的对象obj,retain obj
objc_initWeak(dst, obj); // 调用objc_initWeak 方法
objc_release(obj); // release obj
}
objc_destroyWeak:weak变量释放
void
objc_destroyWeak(id *location)
{
(void)storeWeak
(location, nil);
}
主要就两个方法:
1、设置方法:storeWeak
2、读取方法:objc_loadWeakRetained(ARC)、objc_loadWeak(MRC)
StoreWeak源码
template
static id storeWeak(id *location, objc_object *newObj) {
// 断言判断
assert(haveOld || haveNew);
if (!haveNew) assert(newObj == nil);
// 临时记录这个过程中正在初始化initialize的类
Class previouslyInitializedClass = nil;
id oldObj;
// SideTable就是存储对象的weak相关的信息(后面有简单的说明)
SideTable *oldTable;
SideTable *newTable;
retry:
if (HaveOld) {
oldObj = *location; // 获取旧对象
oldTable = &SideTables()[oldObj]; // 获取旧对象的SideTable
} else {
oldTable = nil;
}
if (HaveNew) {
newTable = &SideTables()[newObj];// 获取新对象的SideTable
} else {
newTable = nil;
}
// 加锁操作,防止多线程中竞争冲突
SideTable::lockTwo(oldTable, newTable);
// 避免线程冲突重处理
// location 应该与 oldObj 保持一致,如果不同,说明被其他线程所修改,goto retry
if (HaveOld && *location != oldObj) {
SideTable::unlockTwo(oldTable, newTable);
goto retry;
}
// 防止弱引用间死锁
// 并且通过 +initialize 初始化构造器保证所有弱引用的 isa 非空指向
if (HaveNew && newObj) {
// 获得新对象的 isa 指针
Class cls = newObj->getIsa();
// 判断 isa 非空且已经初始化
if (cls != previouslyInitializedClass &&
!((objc_class *)cls)->isInitialized()) {
// 解锁
SideTable::unlockTwo(oldTable, newTable);
// 对其 isa 指针进行初始化
_class_initialize(_class_getNonMetaClass(cls, (id)newObj));
// 如果该类已经完成执行 +initialize 方法是最理想情况
// 如果该类 +initialize 在线程中
// 例如 +initialize 正在调用 storeWeak 方法
// 需要手动对其增加保护策略,并设置 previouslyInitializedClass 指针进行标记
previouslyInitializedClass = cls;
// 重新尝试
goto retry;
}
}
// ❤️❤️❤️(2)清除旧对象weak_table种的location
if (HaveOld) {
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
// ❤️❤️❤️(3) 保存location到新对象的weak_table种
if (HaveNew) {
newObj = (objc_object *)weak_register_no_lock(&newTable->weak_table,
(id)newObj, location,
CrashIfDeallocating);
// 如果弱引用被释放 weak_register_no_lock 方法返回 nil
if (newObj && !newObj->isTaggedPointer()) {
// 标记新对象有weak引用,isa.weakly_referenced = true;
newObj->setWeaklyReferenced_nolock();
}
// 设置location指针指向newObj
*location = (id)newObj;
}
else {
// 没有新值,则无需更改
}
SideTable::unlockTwo(oldTable, newTable);
return (id)newObj;
}
objc_loadWeakRetained源码 (ARC访问weak变量)
id
objc_loadWeakRetained(id *location)
{
id obj;
id result;
Class cls;
SideTable *table;
retry:
obj = *location; // 获取指向的对象
if (!obj) return nil;
if (obj->isTaggedPointer()) return obj;
table = &SideTables()[obj];// 获取对象的SideTable
table->lock(); // 加锁
if (*location != obj) { // 对比一次,是在有其他地方在操作修改
table->unlock();
goto retry;
}
result = obj;
cls = obj->ISA();
if (! cls->hasCustomRR()) {
/*
hasCustomRR判断类或父类是否实现下列方法:
1、retain/release/autorelease
2、retainCount/ _tryRetain
3、_isDeallocating
4、retainWeakReference/allowsWeakReference
❤️❤️❤️(1)一般不会重写这些方法,返回NO,执行系统的Retain
*/
assert(cls->isInitialized());
if (! obj->rootTryRetain()) {
// 如果retain失败,返回nil
result = nil;
}
}
else {
// ❤️❤️❤️(2)有自定义的retain/release
// 先看是否初始化initialize
if (cls->isInitialized() || _thisThreadIsInitializingClass(cls)) {
BOOL (*tryRetain)(id, SEL) = (BOOL(*)(id, SEL))
class_getMethodImplementation(cls, SEL_retainWeakReference);
// 是否实现了retainWeakReference,
if ((IMP)tryRetain == _objc_msgForward) {
result = nil;
}
else if (! (*tryRetain)(obj, SEL_retainWeakReference)) {
// 是否可以retain对象,返回NO,该变量将使用“nil”
result = nil;
}
}
else {
// 没有初始化,先初始化,goto retry
table->unlock();
_class_initialize(cls);
goto retry;
}
}
table->unlock();
return result;
}
objc_loadWeak (MRC访问weak变量)
id objc_loadWeak(id *location) {
if (!*location) return nil;
// 利用自动释放池,对retain的对象进行release操作
return objc_autorelease(objc_loadWeakRetained(location));
}
SideTable、weak_table_t、weak_entry_t、weak_referrer_t
1、SideTable:SideTable *objTable = &SideTables()[obj];
从全局的哈希表SideTables中,利用对象本身地址进行位运算后得到对应下标,取得该对象的弱引用表。SideTables是一个 64 个元素长度的散列表,发生碰撞时,可能一个SideTable中存在多个对象共享一个弱引用表。
2、weak_table_t是全部对象weak表:weak_table_t *weak_table = objTable->weak_table;
3、weak_entry_t是一个对象的信息:weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);(referent就是对象地址)
4、weak_referrer_t是这个对象所有weak变量的地址表:weak_referrer_t *referrers = entry->referrers;
struct SideTable {
spinlock_t slock; // 保证原子操作的自旋锁
RefcountMap refcnts; // 引用计数的 hash 表
weak_table_t weak_table; // weak 引用全局 hash 表
}
struct weak_table_t {
weak_entry_t *weak_entries;// 哈希表结构
size_t num_entries; // weak_entry_t个数
uintptr_t mask;// 总容量-1,用来弄哈希值的 (n& mask)就是“除留取余法”(n%总容量)
uintptr_t max_hash_displacement;// 碰撞次数
};
typedef objc_object ** weak_referrer_t;
struct weak_entry_t {
DisguisedPtr referent;
union {
struct {
weak_referrer_t *referrers; // 哈希表结构
uintptr_t out_of_line : 1; // 1是referrers存储,0是inline_referrers
uintptr_t num_refs : PTR_MINUS_1;// 已存referrer总数
uintptr_t mask; // 总容量-1,用来弄哈希值的 (n& mask)就是“除留取余法”(n%总容量
uintptr_t max_hash_displacement; // 碰撞次数
};
struct {
// 如果weak变量少,用数组来存放,如果大于4个,就使用referrers
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
};
}
}
上面三层结构下来,referent就是对象地址,referrers是这个对象所有weak变量的地址,
SideTable *objTable = &SideTables()[obj];
weak_table_t *weak_table = objTable->weak_table;
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
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;
// 对象地址哈希,得到了对应的index
size_t begin = hash_pointer(referent) & weak_table->mask;
size_t index = begin;
size_t hash_displacement = 0;
// 判断是否找到referent对应的weak_entry_t
while (weak_table->weak_entries[index].referent != referent) {
// 如果发生碰撞,则index依次+1,再次查找
index = (index+1) & weak_table->mask;
if (index == begin) bad_weak_table(weak_table->weak_entries);// 异常,crash提示
hash_displacement++;
// 满足冲撞次数,直接返回nil
if (hash_displacement > weak_table->max_hash_displacement) {
return nil;
}
}
// 返回取到的weak_entry_t
return &weak_table->weak_entries[index];
}
weak_register_no_lock注册weak信息
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;
// weak变量地址
objc_object **referrer = (objc_object **)referrer_id;
// 判断对象是否是TaggedPointer
if (!referent || referent->isTaggedPointer()) return referent_id;
bool deallocating; // deallocating 新对象是否正在释放
if (!referent->ISA()->hasCustomRR()) {
// 没有自定义retain/release的相关方法
deallocating = referent->rootIsDeallocating();
}
else {
// 有自定义retain/release的相关方法
BOOL (*allowsWeakReference)(objc_object *, SEL) =
(BOOL(*)(objc_object *, SEL))
object_getMethodImplementation((id)referent,
SEL_allowsWeakReference);
if ((IMP)allowsWeakReference == _objc_msgForward) {
// 没有现实allowsWeakReference允许弱引用 (NSObject类都是实现的)
return nil;
}
// 允许弱引用 deallocating = !YES;不允许弱引用deallocating = !NO
deallocating =
! (*allowsWeakReference)(referent, SEL_allowsWeakReference);
}
// 如果对象正在释放
if (deallocating) {
if (crashIfDeallocating) {
// crash提示
_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 *entry; // 获取对象对应的weak_entry_t
if ((entry = weak_entry_for_referent(weak_table, referent))) {
// append_referrer把weak变量地址,加入weak_entry_t
// (1)先用数组inline_referrers存储(count=4),满了用referrers存储
// (2)如果referrers到了3/4容量,就扩容2倍,重新存回去
// (3)如果referrers没到3/4容量,直接存入referrers,碰撞处理(哈希值++)
append_referrer(entry, referrer);
}
else {
// 新建一个weak_entry_t,保存weak地址
weak_entry_t new_entry(referent, referrer);
// 如果weak_table满容,进行自增长,到了3/4就扩容2倍
weak_grow_maybe(weak_table);
// new_entry插入weak_table,碰撞处理(哈希值++)
weak_entry_insert(weak_table, &new_entry);
}
// 返回新对象
return referent_id;
}
weak_unregister_no_lock解除weak信息
void
weak_unregister_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id)
{
// 对象
objc_object *referent = (objc_object *)referent_id;
// weak地址
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地址
remove_referrer(entry, referrer);
// 判断referrers是否为空
bool empty = true;
if (entry->out_of_line() && entry->num_refs != 0) {
// 判断weak_entry_t 使用了referrers存储weak指针且数量>0,说明还有weak指针
empty = false;
}
else {
// 使用内联数组inline_referrers存储 (WEAK_INLINE_COUNT=4)
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i]) {
empty = false;
break;
}
}
}
if (empty) {
// 如果referrers为空,weak_table删除entry
weak_entry_remove(weak_table, entry);
}
}
}
weak_clear_no_lock清除对象的weak_entry_t、设置referrers中所有的weak地址指向nil
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) {
return;
}
weak_referrer_t *referrers;
size_t count; // 总容量
// 获取weak地址数组,分为:inline_referrers与referrers
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) {
// weak地址全部设置为nil
if (*referrer == 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_table移除entry
weak_entry_remove(weak_table, entry);
}
《Objective-C高级编程 iOS与OS X多线程和内存管理》有一段观点:
“使用附有__weak (NSLog(@"%@",obj1);)修饰符的变量,即使是使用注册到autoreleasepool”
{
id tmp = objc_loadWeakretained(&obj1);
_objc_rootAutorelease(tmp);
NSLog(@"%@",tmp);
}
过时了,现在如下:
{
id temp = objc_loadWeakRetained(&weakObj);
NSLog(@"=====%@",temp);
objc_release(temp);
}
对象的释放过程,在释放过程的最后调用weak_clear_no_lock(&table.weak_table, (id)this);清除weak Hash表,并且所有的weak指针赋值nil
dealloc
- 调用 -release :isa.bits.extra_rc由0继续减一时候触发dealloc,
(1) 标记对象isa.deallocating = true,对象正在被销毁,生命周期即将结束,不能再有新的 __weak 弱引用.
(2) 调用 [self dealloc] (MRC需要在dealloc方法中手动释放强引用的变量)
(3) 继承关系中每一层的父类 都在调用 -dealloc,一直到根类(一般都是NSObject)- NSObject 调 -dealloc
(1)只做一件事:调用 Objective-C runtime 中的 object_dispose() 方法- 调用 object_dispose()
(1) objc_destructInstance(obj);
(2 )free(obj);- objc_destructInstance(obj)执行三个操作
(1) if (cxx) object_cxxDestruct(obj); // 释放变量
1、strong修饰的变量执行objc_storeStrong(&ivar, nil)release对象,ivar赋值nil,
2、weak修饰的变量执行objc_destroyWeak(&ivar) >> storeWeak(&ivar, nil ) >> weak_unregister_no_lock,将变量指向nil,且删除变量对象的weak相关信息(referrers移除weak地址)。
(2) if (assoc) _object_remove_assocations(obj); // 移除Associate关联数据(这就是不需要手动移除的原因)
(3) obj->clearDeallocating(); // 执行weak_clear_no_lock,清空引用计数表、清空weak变量表且将所以引用指向nil