####__weak 的作用是什么?
先给出一段代码 分别用 __strong __weak __unsafe_unretained 指向这个Persion 对象时控制台输出内容分别是什么?
__strong 指向该 Person 对象时 最后Person 才被 dealloc 说明 __strong是个强引用指针 Preson 对象在作用域结束后并没有被立即释放掉
2019-02-19 16:27:04.909648+0800 objc-debug[25362:21505212] 111111111111
2019-02-19 16:27:04.910063+0800 objc-debug[25362:21505212] 22222222222-----
2019-02-19 16:27:04.910085+0800 objc-debug[25362:21505212] Person dealloc
__weak 指向该 Person 对象时 Person 在方法作用域结束时就被释放掉 __weak是一个弱指针 同时打印 p2指针地址 为 null
2019-02-19 16:29:39.860058+0800 objc-debug[26138:21512702] 111111111111
2019-02-19 16:29:39.860354+0800 objc-debug[26138:21512702] Person dealloc
2019-02-19 16:29:39.860384+0800 objc-debug[26138:21512702] 22222222222-----(null)
__unsafe_unretained 指向该 Person 对象时 Person 在方法作用域结束时就被释放掉 __unsafe_unretained也是一个弱指针 同时打印 p2指针地址 不为 null ,如果在后续使用到 p3 这个指针时候 会出现坏内存访问 因为这个指针指向的P erson 对象已经被释放了
2019-02-19 16:29:39.860058+0800 objc-debug[26138:21512702] 111111111111
2019-02-19 16:29:39.860354+0800 objc-debug[26138:21512702] Person dealloc
2019-02-19 16:29:39.860384+0800 objc-debug[26138:21512702] 22222222222-----(null)
通过上边的例子可以得出结论 __weak指向对象释放后 指针会置为 nil 避免了成为野指针造成坏内存访问
源码部分来自苹果官方objc4_723
阅读源码顺序如下
1 (NSObject.mm) _objc_rootDealloc(id obj)
2 (objc-object.h) inline void objc_object::rootDealloc()
3 (objc-runtime-new.mm) id object_dispose(id obj)
4 (objc-runtime-new.mm) void *objc_destructInstance(id obj)
5 (objc-object.h) objc_object::clearDeallocating()
6 (NSObject.mm) objc_object::clearDeallocating_slow()
7 (objc-weak.mm) weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
首先判断对象 dealloc 时满足快速释放条件 , 当这个对象没有关联属性 __weak 指针指向 没有C++析构函数 等条件时 可以直接 free 释放 ,否则就进行下一步处理释放的工作
{
if (isTaggedPointer()) return; // fixme necessary?
/* nonpointer 0 代表普通的指针 0,代表普通的指针,存储着Class、Meta-Class对象的内存地址
1,代表优化过,使用位域存储更多的信息
has_assoc 是否有关联对象 如果没有释放会很快
has_cxx_dtor 是否有C++析构函数 如果没有释放会更快
weakly_referenced 释放有被弱引用指向 如果没有 释放会更快
has_sidetable_rc 引用计数器是否过大无法存储在isa中
如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
*/
/// 快速释放对象 满足上边的条件
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
} else { ///不能够快速是否 需要进一步做释放处理
object_dispose((id)this);
}
}
####id object_dispose(id obj)
id
object_dispose(id obj)
{
if (!obj) return nil;
/// 释放对象前 要将对象的关联属性 weak 指针置空 修改引用计数 isa 指针的一些信息
objc_destructInstance(obj);
free(obj);
return nil;
}
/***********************************************************************
* objc_destructInstance
*在不释放内存的情况下销毁实例。
调用c++析构函数。
*调用ARC ivar清除。
删除关联引用。
*返回的obj。如果obj为nil,则什么也不做。
**********************************************************************/
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
/// 是否有C++析构函数
bool cxx = obj->hasCxxDtor();
/// 是否有关联属性
bool assoc = obj->hasAssociatedObjects();
// This order is important. 这个顺序很重要
if (cxx) object_cxxDestruct(obj); /// 如果有析构函数 去处理下
if (assoc) _object_remove_assocations(obj); ///如果有关联的属性 那么就移除掉所有关联属性
///明确回收
obj->clearDeallocating();
}
return obj;
}
objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
} else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
// Slow path for non-pointer isa with weak refs and/or side table data.
clearDeallocating_slow();
}
assert(!sidetable_present());
}
objc_object::clearDeallocating_slow()
{
assert(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
SideTable& table = SideTables()[this];
table.lock(); ///加锁
if (isa.weakly_referenced) { /// 有弱引用指向
weak_clear_no_lock(&table.weak_table, (id)this);
}
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
table.unlock(); ///解锁
}
下一篇我会单独去讲解 SideTable 里边 weak_table 具体做了些什么
iOS weak 指针实现原理2
好了,我是大兵布莱恩特,欢迎加入博主技术交流群,iOS 开发交流群