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);
*/
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<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
objc_storeWeak:用strong对象赋值
id
objc_storeWeak(id *location, id newObj)
{
return storeWeak<DoHaveOld, DoHaveNew, DoCrashIfDeallocating>
(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<DoHaveOld, DontHaveNew, DontCrashIfDeallocating>
(location, nil);
}
主要就两个方法storeWeak和objc_loadWeakRetained
StoreWeak源码
template <bool HaveOld, bool HaveNew, bool CrashIfDeallocating>
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<HaveOld, HaveNew>(oldTable, newTable);
// 避免线程冲突重处理
// location 应该与 oldObj 保持一致,如果不同,说明被其他线程所修改,goto retry
if (HaveOld && *location != oldObj) {
SideTable::unlockTwo<HaveOld, HaveNew>(oldTable, newTable);
goto retry;
}
// 防止弱引用间死锁
// 并且通过 +initialize 初始化构造器保证所有弱引用的 isa 非空指向
if (HaveNew && newObj) {
// 获得新对象的 isa 指针
Class cls = newObj->getIsa();
// 判断 isa 非空且已经初始化
if (cls != previouslyInitializedClass &&
!((objc_class *)cls)->isInitialized()) {
// 解锁
SideTable::unlockTwo<HaveOld, HaveNew>(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<HaveOld, HaveNew>(oldTable, newTable);
return (id)newObj;
}
objc_loadWeakRetained源码
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()) {
// 没有自定义retain/release,调用系统的Retain
assert(cls->isInitialized());
if (! obj->rootTryRetain()) {
// 如果retain失败,返回nil
result = nil;
}
}
else {
// 有自定义的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;
}
SideTable、weak_table_t、weak_entry_t
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 {
DisguisedPtrobjc_object> 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; // 同上
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;
if (!referent || referent->isTaggedPointer()) return referent_id;
// deallocating 对象是否正在释放
bool 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允许弱引用
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))) {
// 把weak变量地址,加入weak_entry_t
// 先用数组inline_referrers存储,满了用哈希表referrers
// 如果referrers到了3/4容量,就扩容2倍,重新存回去
// 没满,直接存入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) {
empty = false;
}
else {
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) {
// objc源码测试,全在这里return了,后面一坨代码没有走。不知道什么问题?
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
1. 调用 -release :isa.bits.extra_rc由0继续减一时候触发dealloc,
* 标记对象isa.deallocating = true,对象正在被销毁,生命周期即将结束.
* 不能再有新的 __weak 弱引用.
* 调用 [self dealloc] (MRC需要在dealloc方法中手动释放强引用的变量)
* 继承关系中每一层的父类 都在调用 -dealloc,一直到根类(一般都是NSObject)
2. NSObject 调 -dealloc
* 只做一件事:调用 Objective-C runtime 中的 object_dispose() 方法
3. 调用 object_dispose()
(1)objc_destructInstance(obj);
(2)free(obj);
4. objc_destructInstance(obj)执行三个操作
1、if (cxx) object_cxxDestruct(obj); // 释放变量
(1) strong的objc_storeStrong(&ivar, nil)release对象,ivar赋值nil,
(2) weak ivar,出了作用域,objc_destroyWeak(&ivar) >> storeWeak(&ivar, nil) 将ivar指向nil且ivar的地址从对象的weak表中删除。
2、if (assoc) _object_remove_assocations(obj); // 移除Associate关联数据(这就是不需要手动移除的原因)
3、obj->clearDeallocating(); // 清空引用计数表、清空weak变量表且将所以引用指向nil
主要参考:
weak 弱引用的实现方式
ObjC Runtime 中 Weak 属性的实现 (上)
ObjC Runtime 中 Weak 属性的实现 (中)
网友评论