当我使用 __ 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的retainCount预计为1,为什么retainCount为2?
我已经从书中读到:__ weak指针将对象注册到 autoreleasepool ,因此保留计数为+。
然而,obj1和obj2指的是相同的内存地址,在这种情况下,obj1的retainCount也应该变为2.但是,它仍然保留在1中。
我知道retainCount是不可靠的,但我很好奇这是怎么来的。 (我的环境是Xcode 8.3.3,iOS 10.3)
非常感谢任何人都可以向初学者解释这一点:)
答案 0 :(得分:6)
保留计数的确切值通常是一个实现细节,它依赖于编译器,Objective-C语言运行库以及所涉及的任何其他库(如Foundation)。但是,只要你不依赖于这种行为,就可以理解它为什么会这样做。
你的书是错的,或者你误解了它。使用__weak
并不会将对象放入autoreleasepool。
以下是第32行(具有@"obj2: %ld"
格式字符串的那个)的情况。
为了安全地将对象引用传递给函数或方法(如NSLog
),它必须是一个强引用的引用。因此,编译器会在obj2
上生成对objc_loadWeakRetained
的调用。此函数以原子方式递增对象的保留计数并返回引用。因此,在将对象引用传递给NSLog
之前,对象的保留计数从1到2。
然后,在NSLog
返回后,编译器生成对objc_release
的调用。因此,保留计数从第3个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
的来电,并在objc_release
之后致电NSLog
。