博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从@property说起(二)当我们写下@property (nonatomic, weak) id obj时,我们究竟写了什么...
阅读量:6189 次
发布时间:2019-06-21

本文共 2664 字,大约阅读时间需要 8 分钟。

@property (nonatomic, weak) id obj;

先看下面一段代码

#import 
@interface TestClass : NSObject@property (nonatomic, weak) id foo;@end@implementation TestClass@synthesize foo = _foo;- (id)foo { return _foo;}- (void)setFoo:(id)foo { _foo = foo;}@endint 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修饰符。当我们声明一个weak属性foo时,实际是声明了一个__weak id _foo;

于是我们真正要讨论的,是__weak关键字。
实际上这个问题随便搜一下有很多答案,大部分答案都来自于iOS业界著名的一本书——Kazuki Sakamoto所著《Objective-C高级编程》。这本书的确很好,但是,在有些地方个人认为跳跃太大,导致读者容易出现误解。__weak关键字的实现部分就是如此。
首先我们先看书中的解释:

{    id __weak obj1 = obj;}

假设obj附加__strong修饰符并被赋值。

编译器会把这段代码转换成运行时代码

id obj1;  objc_initWeak(&obj1, obj);  objc_destoryWeak(&obj1);

即编译器会通过objc_initWeak函数初始化__weak修饰的变量,当变量的作用域结束后会通过objc_destoryWeak函数释放该变量。objc_initWeak函数实际干的活是:

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置空了。

所以一步步分析:

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)函数,而这个函数的实现是:

voidobjc_destroyWeak(id *location){    (void)storeWeak
(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了

转载地址:http://cgoda.baihongyu.com/

你可能感兴趣的文章
leetcode 451. Sort Characters By Frequency
查看>>
Sparse Feature Learning
查看>>
selenium - webdriver - ActionChains类(鼠标操作)
查看>>
续上篇关于任务分派的方式
查看>>
[CodeForces - 919B] Perfect Number
查看>>
方法重载-定义三个方法,实现int double 和数组类型的问题
查看>>
DOM之基础简介
查看>>
优雅降级和渐进增强?
查看>>
eclipse, Log4j配置(真心的详细~)
查看>>
struts2+jquery+ajax实现上传&&校验实例
查看>>
Amend Last Commit选项使用注意点
查看>>
android 收集的一些颜色值
查看>>
最小化安装CentOS 6.3
查看>>
Socket and JSON
查看>>
弹出窗口插件
查看>>
nginx查看php-fpm 工作状态
查看>>
Javascript 的addEventListener()及attachEvent()区别分析
查看>>
leetcode560
查看>>
java web学习总结(二十三) -------------------编写自己的JDBC框架
查看>>
日期格式化工具方法
查看>>