当前位置: 首页 > news >正文

从@property说起(二)当我们写下@property (nonatomic, weak) id obj时,我们究竟写了什么...

@property (nonatomic, weak) id obj;

先看下面一段代码

#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修饰符。当我们声明一个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)函数,而这个函数的实现是:

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了

相关文章:

  • httpclient 优化
  • PHPCMSV9上线方法及文件权限设置
  • 推理题:猜扑克牌
  • JS获取首字母
  • zabbix-----5-----自动发现的概念
  • NTP时钟同步学习记录
  • java基础知识 构造方法
  • 在 Chrome DevTools 中调试 JavaScript 入门
  • IntelliJ IDEA 主题、字体、编辑区主题、文件编码修改
  • mediawiki登录时第一次会跳回登录页面,第二次才能登录成功
  • 更换好的yum源
  • 初识cesium----加载不同JSON格式例子
  • 《React Native高效开发》之create-react-native-app
  • 算法(Algorithms)第4版 练习 2.3.25
  • Matlab中imnoise函数的用法
  • 【译】理解JavaScript:new 关键字
  • 2017-09-12 前端日报
  • Babel配置的不完全指南
  • classpath对获取配置文件的影响
  • CSS实用技巧
  • EventListener原理
  • Theano - 导数
  • win10下安装mysql5.7
  • 复杂数据处理
  • 基于HAProxy的高性能缓存服务器nuster
  • 怎么把视频里的音乐提取出来
  • 正则表达式
  • MyCAT水平分库
  • PostgreSQL 快速给指定表每个字段创建索引 - 1
  • Unity3D - 异步加载游戏场景与异步加载游戏资源进度条 ...
  • 树莓派用上kodexplorer也能玩成私有网盘
  • ​Linux Ubuntu环境下使用docker构建spark运行环境(超级详细)
  • # Python csv、xlsx、json、二进制(MP3) 文件读写基本使用
  • #、%和$符号在OGNL表达式中经常出现
  • #Ubuntu(修改root信息)
  • (C语言)输入自定义个数的整数,打印出最大值和最小值
  • (动手学习深度学习)第13章 计算机视觉---图像增广与微调
  • (二)Pytorch快速搭建神经网络模型实现气温预测回归(代码+详细注解)
  • (二)windows配置JDK环境
  • (十八)SpringBoot之发送QQ邮件
  • (十一)图像的罗伯特梯度锐化
  • (一)UDP基本编程步骤
  • (转)setTimeout 和 setInterval 的区别
  • (转载)虚幻引擎3--【UnrealScript教程】章节一:20.location和rotation
  • .mysql secret在哪_MYSQL基本操作(上)
  • .net Stream篇(六)
  • .NET 事件模型教程(二)
  • .NET中的Event与Delegates,从Publisher到Subscriber的衔接!
  • @四年级家长,这条香港优才计划+华侨生联考捷径,一定要看!
  • [ CTF ] WriteUp- 2022年第三届“网鼎杯”网络安全大赛(朱雀组)
  • [ HTML + CSS + Javascript ] 复盘尝试制作 2048 小游戏时遇到的问题
  • [AutoSar]BSW_Com07 CAN报文接收流程的函数调用
  • [AX]AX2012开发新特性-禁止表或者表字段
  • [BZOJ] 1001: [BeiJing2006]狼抓兔子
  • [C#]winform使用引导APSF和梯度自适应卷积增强夜间雾图像的可见性算法实现夜间雾霾图像的可见度增强