先看下面一段代码编程
#import <Foundation/Foundation.h> @interface TestClass : NSObject @property (nonatomic, weak) id foo; @end @implementation TestClass @synthesize foo = _foo; - (id)foo { return _foo; } - (void)setFoo:(id)foo { _foo = foo; } @end int main(int argc, const char * argv[]) { TestClass *obj = [TestClass new]; obj.foo = [NSObject new]; NSLog(@"%@", obj.foo); return 0; }
这段代码应该没有疑问,输出的是null。
那么究竟为何是null?若是你仅仅给出答案说,由于foo这个属性是weak修饰,是否是太过简单了点。
因此,问题要一点点分析,首先就是:函数
这个问题,首先就是要先知道,weak真正的做用,实际是__weak修饰符。当咱们声明一个weak属性foo时,实际是声明了一个__weak id _foo;
因而咱们真正要讨论的,是__weak关键字。
实际上这个问题随便搜一下有不少答案,大部分答案都来自于iOS业界著名的一本书——Kazuki Sakamoto所著《Objective-C高级编程》。这本书的确很好,可是,在有些地方我的认为跳跃太大,致使读者容易出现误解。__weak关键字的实现部分就是如此。
首先咱们先看书中的解释:atom
{ id __weak obj1 = obj; }
假设obj附加__strong修饰符并被赋值。
编译器会把这段代码转换成运行时代码指针
id obj1; objc_initWeak(&obj1, obj); objc_destoryWeak(&obj1);
即编译器会经过objc_initWeak函数初始化__weak修饰的变量,当变量的做用域结束后会经过objc_destoryWeak函数释放该变量。objc_initWeak函数实际干的活是:code
objc1 = 0; objc_storeWeak(&obj1, obj);
这里是先将指针objc1置成0,再调用objc_storeWeak函数使得obj1指向obj对象。
接下来的objc_destoryWeak函数的实际操做以下:对象
objc_storeWeak(&obj1, 0);
总结一下,刚才的整个过程模拟代码以下:作用域
id obj1; obj1 = 0; objc_storeWeak(&obj1, obj); objc_storeWeak(&obj1, 0);
实际上,objc_storeWeak函数会把第二个参数的对象的地址做为key,并将第一个参数(__weak关键字修饰的指针的地址)做为值,注册到weak表中。若是第二个参数为0(说明对应的对象被释放了),则将weak表中将整个key-value键值对删除,这就是__weak关键字的核心思想!
在那本书,到这里讲得都很是好,然而多是由于年代久远,当时runtime也没有开源,因此关于 id __weak obj = [NSObject new]这个问题描述不太清晰。
下面是个人例子:编译器
int main(int argc, const char * argv[]) { NSObject *foo = [NSObject new]; __weak id obj = foo; NSLog(@"%@", obj); obj = [NSObject new]; NSLog(@"%@", obj); return 0; }
显然第一个log是有值的,第二个log应该是null。
那么问题就是,为何第二个log没有办法取到值。
固然这个结果自己就是__weak的特性,咱们要讨论的,就是这个特性是如何实现的。源码
首先,再给obj声明并赋初值时,foo这个局部变量默认就是__strong的,因此,obj能够储存foo的值。这个没有什么疑问。那么关键就在于,下面的obj
= [NSObject new]是不会调用objc_initWeak函数,可是依然成功把obj置空了。it
因此一步步分析:
obj = [NSObject new];
实际转换为
id objc_storeWeak(&obj, newObj)
newObj显然不为0,因而storeWeak函数成功返回了id类型的newObj。
而后关键在于下一步,参数newObj因为没有被任何变量引用,因此newObj的做用域在storeWeak函数返回后,就结束了。
因而
newObj调用下面的函数 void objc_release(id obj) 被成功释放
而在此时,newObj的dealloc是调用了objc_clear_deallocating函数,这个函数实现能够经过runtime源码来查看,其中就包含:
首找出对象对应的weak_entry_t链表,而后挨个将弱引用置为nil。最后清理对象的记录。
这样,就会调起obj的objc_destroyWeak(&obj)函数,而这个函数的实现是:
void objc_destroyWeak(id *location) { (void)storeWeak<true/*old*/, false/*new*/, false/*crash*/> (location, nil); }
第二个参数直接传nil,因而清理obj中的值。
因此,咱们再回头看一下,@property (nonatomic, weak) id obj的setter方法:
- (void)setObj:(id)obj { _obj = obj; }
实际上,setter的实现,在编译完成后,会转换为
objc_storeWeak(&_obj, obj); objc_release(obj);
因此当咱们写下
@property (nonatomic, weak) id obj;
咱们到底写下了什么。
在下一篇中,就须要讲一下nonatomic和atomic了