【问题标题】:iOS10 __weak pointer made retainCount +1iOS10 __weak 指针使retainCount +1
【发布时间】:2018-05-19 06:13:05
【问题描述】:

当我使用 __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,因此保留计数 + 1。

但是,obj1 和 obj2 指向同一个内存地址,这样的话,obj1 的 retainCount 也应该变成 2。但是,它仍然是 1。

我知道 retainCount 是不可靠的,但我很好奇这是怎么来的。 (我的环境是Xcode 8.3.3,iOS 10.3)

如果有人能向初学者解释这一点,不胜感激:)

【问题讨论】:

  • 因为一个实现细节。尝试开启优化。或针对不同的平台。 retainCount 不仅不可靠,而且毫无用处。事实上,输出比你提到的更令人困惑。 obj1obj2 是相同的。这可能与使用/obj2 进行函数调用有关。您可以随时反汇编代码并查看。
  • 如文档所述:不要使用- retainCountCFRetainCount。返回的值不可靠。另见herehere
  • @mschmidt 谢谢!在那个问题中学到了很多东西。
  • @bbum retainCount 比我预期的更不可靠,谢谢!
  • 这个问题不是When to use -retainCount的重复问题。这个问题是关于实现细节的,问实现细节是合理的。

标签: objective-c retaincount


【解决方案1】:

保留计数的确切值通常是一个实现细节,它取决于编译器、Objective-C 语言运行时以及涉及的任何其他库(如 Foundation)。但有理由怀疑它为什么会这样,只要你不依赖这种行为。

要么你的书是错的,要么你误解了它。使用__weak 不会将对象放入自动释放池中。

这是第 32 行发生的事情(带有@"obj2: %ld" 格式字符串的行)。

要安全地将对象引用传递给函数或方法(如NSLog),它必须是强引用。所以编译器会在obj2 上生成对objc_loadWeakRetained 的调用。此函数以原子方式递增对象的保留计数并返回引用。因此,在对象引用被传递到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。您可以在调用CFGetRetainCount 之前看到对objc_loadWeakRetained 的调用,以及在NSLog 返回之后对objc_release 的调用。

【讨论】:

  • 谢谢,罗伯。请注意,生成的程序集(包括保留/发布)将在编译器、发布和架构之间发生变化。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-05
  • 1970-01-01
  • 2011-09-18
  • 2019-05-10
  • 1970-01-01
  • 1970-01-01
  • 2011-01-02
相关资源
最近更新 更多