__weak引发的一点思考

作者: RemisKrlet | 来源:发表于2017-06-04 11:33 被阅读238次

背景介绍

最近在开发中遇到了一个有趣的问题:一个对象A,被对象B的属性a弱引用。在A的dealloc方法中,打印B->_aB.a的值,发现前者正常打印,后者的结果是nil。这很好的勾起了我的好奇心和求知欲(对于__weak在 Runtime的实现理解不够透彻),邃手写demo,查阅资料;经过一番谷歌和LLVM的手册的搜索后,有了答案。

分析

起初以为编译器对weak属性的getter做了特殊处理,如果直接访问成员变量,则有值,不过这个假设在我修改了demo的代码以后就被推翻了:无论是通过getter访问还是直接访问成员变量,二者的值都是nil,只不过在控制台打印的结果二者会有不同。于是问题就变成了两个:weak属性在dealloc里的值为什么是nil?在NSLog等方法输出到控制台以后,以上两种取值方式的输出结果却不同?

对demo中的dealloc部分代码的汇编代码进行了分析,发现了一点猫腻(以下分别是__strong__weak属性)在dealloc中的不同(省略了干扰代码):

 0x10b6df320 <+0>:   pushq  %rbp
 0x10b6df321 <+1>:   movq   %rsp, %rbp
 0x10b6df324 <+4>:   subq   $0x60, %rsp
 0x10b6df328 <+8>:   movq   %rdi, -0x8(%rbp)
 0x10b6df32c <+12>:  movq   %rsi, -0x10(%rbp)
 0x10b6df330 <+16>:  movq   -0x8(%rbp), %rsi
 0x10b6df334 <+20>:  movq   0x2d3d(%rip), %rdi        ; "'t'"
 0x10b6df33b <+27>:  movq   %rdi, -0x40(%rbp)
 0x10b6df33f <+31>:  movq   %rsi, %rdi
 0x10b6df342 <+34>:  movq   -0x40(%rbp), %rsi
 0x10b6df346 <+38>:  callq  0x10b6df940               ; symbol stub for: objc_msgSend
 0x10b6df34b <+43>:  movq   %rax, %rdi
 0x10b6df34e <+46>:  callq  0x10b6df958               ; symbol stub for: objc_retainAutoreleasedReturnValue
 
0x101c17462 <+82>:  movq   %rax, %rdi
0x101c17465 <+85>:  callq  0x101c17986               ; symbol stub for: objc_release
0x101c1746a <+90>:  leaq   0x2be7(%rip), %rax        ; TObject2._ta
    ->  0x101c17471 <+97>:  movq   -0x18(%rbp), %rsi
0x101c17475 <+101>: movq   (%rax), %rax
0x101c17478 <+104>: addq   %rax, %rsi
0x101c1747b <+107>: movq   %rsi, %rdi
0x101c1747e <+110>: callq  0x101c17974               ; symbol stub for: objc_loadWeakRetained

不难发现,对于__weak属性的访问,编译器调用了objc_loadWeakRetained函数,对于这个函数,LLVM文档第4.2节 给出了如下解释:

Reading occurs when performing a lvalue-to-rvalue conversion on an object lvalue.
For __weak objects, the current pointee is retained and then released at the end of the current full-expression. This must execute atomically with respect to assignments and to the final release of the pointee.
For all other objects, the lvalue is loaded with primitive semantics.

If object is registered as a __weak object, and the last value stored into object has not yet been deallocated or begun deallocation, retains that value and returns it. Otherwise returns null.

结合该方法源码上方的一段说明:

Once upon a time we eagerly cleared *referrer if we saw the referent was deallocating. This confuses code like NSPointerFunctions which tries to pre-flight the raw storage and assumes if the storage is zero then the weak system is done interfering.
That is false: the weak system is still going to check and clear the storage later.
This can cause objc_weak_error complaints and crashes.
So we now don't touch the storage until deallocation completes.

结果

通过以上内容,我们知道了这个函数的作用:在__weak属性被访问时,为了确保其结果有值,或者为nil,不会出现__unsafe_unretained属性那种野指针的现象,编译器的处理为retain该对象,并加入自动释放池,在之后使用完毕后再释放。同时在dealloc方法中,此时对象已经处于deallocation的状态,objc_loadWeakRetained函数会返回nil。 第一个问题解决了!关于这个函数的实现,有兴趣的同学可以在objc的源码的objc-weak.mm中了解下,这里就不再对源码进行讲解。

对于问题二,目前还在调研中,我的猜测是编译器对getter和成员变量取值这两种形式做了特殊处理,使用了Code Generate技术。由于成员变量的释放与内存清理会在dealloc的末尾,也就是调用[super dealloc]中的 object_dispose函数中执行,因此编译器察觉到了这一点,为了方便Log,就保留了原值。 如果你有更好的解答,请在评论中回复我,非常感谢。

相关文章

  • __weak引发的一点思考

    背景介绍 最近在开发中遇到了一个有趣的问题:一个对象A,被对象B的属性a弱引用。在A的dealloc方法中,打印B...

  • 修饰符weak引发的思考

    背景 在测试中发现有个网页没有加载出来,查找原因时,发现是由于用了weak修饰了一个NSURL的全局变量,出了方法...

  • 对 Strong-Weak Dance的思考

    对 Strong-Weak Dance的思考 对 Strong-Weak Dance的思考

  • Strong-Weak Dance

    对 Strong-Weak Dance 的思考 在使用Block时,除了使用__weak修饰符避免循环引用外,还有...

  • 一点纠结引发的思考

    昨天看一本叫做《把力气花在你想要的生活上》的一本书,作者是一个科技的体验者,他提到说苹果手机的设计和Macbook...

  • 疫情引发的一点思考

    这注定是一个不同寻常的春节。 小区里很安静,少有人出行,大家从网络上获知各地疫情,知道形势严峻,便都安静地宅在家里...

  • 打卡引发的一点思考

    2021-06-30 阴有阵雨 周三 “给宝贝打卡了吗?”早餐前,我问老婆。 “早打了,我每天都定...

  • swift中循环引用的处理 weak与unowned

    2018年2月26日更新 最近在项目用到了 weak | unowned 又有些思考.大部分情况推荐使用weak ...

  • 一点问题引发的一点思考

    一点问题引发的一点思考 在最近的教学过程中,发现了一些问题。 1. 部分学生行为习惯存在问题。行为习惯存在的问题表...

  • 关于民谣引发的一点思考

    昨天晚上和孟老师看了我歌的最近一期。民谣最新代言人赵雷唱了一首《理想》。镜头扫到观众席,台下几乎是眼泪的汪洋。多是...

网友评论

  • 进击的iOS开发:我也遇到类似问题,感觉就是因为objc_loadWeakRetained函数导致地问题。B->_a和B.a不同地问题应该就是 objc_loadWeakRetained 有没有调用,如果调用了还要确保 对象地引用计数为0

本文标题:__weak引发的一点思考

本文链接:https://www.haomeiwen.com/subject/opyvfxtx.html