前言
OC
中的 weak
属性是怎么实现的,为什么在对象释放后会自动变成 nil?本文对这个问题进行了一点探讨。
环境
mac OS Sierra 10.12.4
objc709
参考答案
搜索后发现runtime 如何实现 weak 属性给出了一个参考答案。
runtime
对注册的类, 会进行布局,对于weak
对象会放入一个hash
表中。 用weak
指向的对象内存地址作为key
,当此对象的引用计数为0
的时候会dealloc
,假如weak
指向的对象内存地址是a
,那么就会以a
为键, 在这个weak
表中搜索,找到所有以a
为键的weak
对象,从而设置为nil
。
测试
代码
#import <Foundation/Foundation.h>
@interface WeakProperty : NSObject
@property (nonatomic,weak) NSObject *obj;
@end
@implementation WeakProperty
- (void)dealloc {
NSLog(@"%s",__func__);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
WeakProperty *property = [[WeakProperty alloc] init];
NSObject *obj = [[NSObject alloc] init];
property.obj = obj;
NSLog(@"%@",property.obj);
// 会触发函数 ``id objc_initWeak(id *location, id newObj)``
// NSObject *obj = [[NSObject alloc] init];
// __weak NSObject *obj2 = obj;
// 会触发函数 ``void objc_copyWeak(id *dst, id *src)``
// __weak NSObject *obj3 = obj2;
}
return 0;
}
结果
对象的 weak
属性调用 setter
时
- 调用
id objc_storeWeak(id *location, id newObj)
- 调用
static id storeWeak(id *location, objc_object *newObj)
…
使用 NSLog
输出 property.obj
属性时
- 调用
id objc_loadWeakRetained(id *location)
当 dealloc
释放对象时
- 调用
void objc_destroyWeak(id *location)
相关函数
查看 NSObject.mm
源码发现
id objc_storeWeak(id *location, id newObj)
id objc_storeWeakOrNil(id *location, id newObj)
id objc_initWeak(id *location, id newObj)
id objc_initWeakOrNil(id *location, id newObj)
void objc_destroyWeak(id *location)
都调用了 static id storeWeak(id *location, objc_object *newObj)
, objc_xxxWeakOrNil
多了一点额外的处理,但并不影响整体的理解。而 void objc_destroyWeak(id *location)
在调用 static id storeWeak(id *location, objc_object *newObj)
时 newObj
参数传递的是 nil
这一点与上面提到的参考答案中关于 dealloc
释放对象时,将哈希表中指定的键对应的值设置为 nil
是符合的。
小结
storeWeak
函数用于为weak
属性赋值 (包括销毁)objc_loadWeakRetained
函数用于获取weak
属性
观察 & 分析
对于函数 storeWeak
主要分析两种情况下的调用
- 赋值,即
id objc_storeWeak(id *location, id newObj)
- 销毁,即
void objc_destroyWeak(id *location)
而对于 weak
属性的获取主要分析
- 函数
id objc_loadWeakRetained(id *location)
观察: id objc_storeWeak(id *location, id newObj)
/**
* This function stores a new value into a __weak variable. It would
* be used anywhere a __weak variable is the target of an assignment.
*
* @param location The address of the weak pointer itself
* @param newObj The new object this weak ptr should now point to
*
* @return \e newObj
*/
id
objc_storeWeak(id *location, id newObj)
{
return storeWeak<DoHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object *)newObj);
}
该函数单纯的调