【问题标题】:Why Obj-C instance have 1 retain count Just created?为什么 Obj-C 实例有 1 个保留计数刚刚创建?
【发布时间】:2016-11-16 23:03:28
【问题描述】:

我在学习 obj-c/swift arc 系统。通过 CFGetRetainCount 函数打印创建实例的保留计数日志。

我期望像这样的引用计数

let foo1 = NSObject()  // foo1 retain count 1
let foo2 = foo1        // foo1 retain count 2
foo2 = nil             // foo1 retains count 1
foo1 = nil             // foo1 retain count 0. release

但实际上..

let foo1 = NSObject()  // foo1 retain count 2
let foo2 = foo1        // foo1 retain count 3
foo2 = nil             // foo1 retain count 2
foo1 = nil             // foo1 retain count 1. release

并直接打印 NSObject() 的保留计数..

print(CFGetRetainCount(NSObject()))  // retain count 1

基本上, NSObject() 有 1 个保留计数。并在保留计数达到 2 时释放对象。

我知道当与实例强链接时会增加保留计数。但是刚刚创建的实例有1个retain count,当retain count变为1而不是0时释放实例,这些现象的原因是什么?

【问题讨论】:

  • 您的代码甚至没有编译,因为foo1foo2 是常量。即使您将它们声明为变量,您也不能将 nil 分配给它们。你真正测试了什么?
  • 我从不认为 CFGetRetainCount 可以向我展示对象保留计数。尝试简单的代码: var foo1: NSNumber? = NSNumber(值:0)打印(CFGetRetainCount(foo1))。打印的数字很大
  • 你在问一个关于代码的问题。发布您所询问的代码是一种常见的礼貌,而不是您当场编造的代码。
  • www.whentouseretaincount.com
  • @bbum 该网站不再工作了。

标签: ios objective-c swift automatic-ref-counting


【解决方案1】:

TL;DR您记录的方式会影响结果,您可能会在记录时获得临时所有权,从而将所有打印结果增加 1。

以下答案是一个简化的答案,因为您不必担心retainCount 的实际价值。

保留计数记录给定对象有多少引用(所有者)。 创建时,只有一个所有者,因此保留计数设置为 1。每次对象获得新所有者 (retain),保留计数增加 1。每次对象丢失和所有者 (release) 时,保留计数都会减一。

请注意,retainCount 永远不会为零。如果所有者的数量为 1 并且您失去了所有者,则对象被释放,然后计数不会减少。

为了更好的测试,我创建了一个 Obj-C 类,在没有 ARC 的情况下编译:

@implementation TestObject

- (instancetype)init {
    TestObject *result = [super init];

    NSLog(@"Retain count after creation: %@", @(self.retainCount));

    return result;
}

- (instancetype)retain {
    TestObject *result = [super retain];
    NSLog(@"Retain count after retain: %@", @(self.retainCount));

    return result;
}

- (oneway void)release {
    NSLog(@"Retain count before release: %@", @(self.retainCount));

    [super release];
}

- (void)dealloc {
    NSLog(@"Retain count before dealloc: %@", @(self.retainCount));
    [super dealloc];
}

@end

并在 Swift 中使用它而不是你的 NSObject:

var foo1: TestObject? = TestObject()
print("#")
print(CFGetRetainCount(foo1))
var foo2 = foo1
print("#")
print(CFGetRetainCount(foo1))
foo2 = nil
print("#")
print(CFGetRetainCount(foo1))
foo1 = nil

导致:

Retain count after creation: 1
#
Retain count after retain: 2
2
Retain count before release: 2
Retain count after retain: 2
#
Retain count after retain: 3
3
Retain count before release: 3
Retain count before release: 2
#
Retain count after retain: 2
2
Retain count before release: 2
Retain count before release: 1
Retain count before dealloc: 1

这基本上是您所期望的,但是当您在将对象传递给函数时获得临时所有权时,每个 CFGetRetainCount 周围都会有额外的保留和释放。

这是您永远不应该阅读retainCount 值的示例之一。没有调试价值,documentation中也有提到。

【讨论】:

    【解决方案2】:

    我研究了 obj-c/swift arc 系统。

    如果您试图理解 ARC 和保留计数如何在高水平上工作,那么您将走错路。请参阅hereherehere

    这些现象的原因是什么?

    在编译器中,ARC 是一个非常复杂的系统,底层有许多优化层。很难理解实际的保留计数值。此外,它还受优化级别的影响。

    如果您真的想深入了解,这里是 Swift 中的 paper on ARC

    【讨论】:

      【解决方案3】:

      简而言之:它必须至少为 1,因为当保留计数变为 0 时,对象消失(因此没有对象询问其保留计数是多少)。 p>

      更长的答案可能是我很久以前为朋友做的这个视频:https://www.youtube.com/watch?v=cBN--I31Xjo

      但实际上,如果您使用的是 ARC,则不应该查看保留计数。 ARC 会根据需要插入保留,此时 ARC 保留对象 5 次是完全没问题的,只要稍后也释放 5 次即可。

      这就是-retainCountCFGetRetainCount() 的问题:由于保留计数的全部意义在于共享所有权,因此您不应该关心还有谁拥有引用,您应该只关心您的代码拥有多少引用以及它通过确保它没有循环强引用来正确放弃它们。

      【讨论】:

        【解决方案4】:

        我认为这是因为当您将对象传递给 CFGetRetainCount 函数或任何函数时,保留计数应增加一。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-06-07
          • 1970-01-01
          • 1970-01-01
          • 2020-04-17
          • 1970-01-01
          • 1970-01-01
          • 2014-02-21
          相关资源
          最近更新 更多