当我使用__weak指针引用NSObject时,会显示意外的retainCount。
测试代码和结果如下图所示。

以下是代码:
id obj1 = [[NSObject alloc] init];
id __weak obj2 = obj1;
NSLog(@"obj1: %ld", CFGetRetainCount((__bridge CFTypeRef)obj1)); // line 31
NSLog(@"obj2: %ld", CFGetRetainCount((__bridge CFTypeRef)obj2)); // line 32
NSLog(@"obj1 again: %ld", CFGetRetainCount((__bridge CFTypeRef)obj1)); // line 33所以~我的困惑是,obj2 2的retainCount预期为1,为什么retainCount是2?
我从书中读到:autoreleasepool,指针将对象注册到__weak,因此保留计数+ 1。
然而,obj1和obj2引用相同的内存地址,在这种情况下,obj1 1的retainCount也应该变成2,但是,它仍然保留在1中。
我知道retainCount不可靠,但我很好奇这是怎么回事。(我的环境是Xcode 8.3.3,iOS 10.3)
非常感谢任何人能向初学者解释这一点:)
发布于 2017-12-05 16:31:36
保留计数的确切值通常是依赖于编译器、Objective语言运行库和任何其他相关库(如Foundation)的实现细节。但是,只要你不依赖于行为,你就有理由怀疑它为什么会这样表现。
要么你的书错了,要么你误解了它。使用__weak不会将对象放在自动发布池中。
下面是第32行正在发生的事情(使用@"obj2: %ld"格式字符串)。
要安全地将对象引用传递给函数或方法(如NSLog),它必须是一个强保存的引用。所以编译器在obj2上生成对obj2的调用。这个函数原子地增加对象的保留计数,并返回引用。因此,在将对象引用传递给NSLog之前,对象的保留计数从1到2。
然后,在NSLog返回后,编译器将生成对objc_release的调用。因此,第三个NSLog将保留计数从2降到1。
如果您想确定发生了什么,请查看编译器的汇编程序输出。您可以要求Xcode向您展示它:

以下是程序集的相关部分:
.loc 2 0 9 discriminator 1 ## /Users/mayoff/TestProjects/test/test/main.m:0:9
leaq -32(%rbp), %rdi
##DEBUG_VALUE: obj2 <- [%RBP+-32]
.loc 2 16 66 is_stmt 1 ## /Users/mayoff/TestProjects/test/test/main.m:16:66
callq _objc_loadWeakRetained
movq %rax, %rbx
Ltmp4:
.loc 2 16 29 is_stmt 0 ## /Users/mayoff/TestProjects/test/test/main.m:16:29
movq %rbx, %rdi
callq _CFGetRetainCount
movq %rax, %rcx
Ltmp5:
Ltmp20:
## BB#3:
##DEBUG_VALUE: obj2 <- [%RBP+-32]
##DEBUG_VALUE: obj1 <- %R15
Ltmp6:
.loc 2 16 9 discriminator 1 ## /Users/mayoff/TestProjects/test/test/main.m:16:9
leaq L__unnamed_cfstring_.4(%rip), %rdi
xorl %eax, %eax
movq %rcx, %rsi
callq _NSLog
Ltmp7:
Ltmp21:
## BB#4:
##DEBUG_VALUE: obj2 <- [%RBP+-32]
##DEBUG_VALUE: obj1 <- %R15
.loc 2 16 9 discriminator 2 ## /Users/mayoff/TestProjects/test/test/main.m:16:9
movq %rbx, %rdi
callq *_objc_release@GOTPCREL(%rip)第16行(在我的测试中)是使用obj2的。您可以在调用objc_loadWeakRetained之前看到对CFGetRetainCount的调用,在NSLog返回后看到对objc_release的调用。
https://stackoverflow.com/questions/47656074
复制相似问题