【问题标题】:How do I verify reference count in ARC mode?如何在 ARC 模式下验证引用计数?
【发布时间】:2012-02-10 09:44:40
【问题描述】:

我曾经在调试器下使用 [myVar retainCount] 来验证我的一些变量是否具有预期的保留计数,尤其是对于没有自定义 dealloc 的 var。

在 ARC 模式下如何做到这一点?如何确保没有内存泄漏?

注意:我知道 ARC 应该为我处理这个问题,但生活远非完美,在现实生活中,您的对象有时由第三方库分配(使用保留?)并且从未被释放。

我这样做的图片:

MyObj *myObj=[[MyObj alloc] init];

然后我打电话

[somethingElse doSomethingWithMyObj:myObj];

后来,我愿意

myObj=NULL;

如果我的程序运行良好,我的预期是 myObj 被销毁,但似乎并非如此......

那么我该如何跟踪这个,尤其是如果 somethingElse 不是由我管理的?

现在,关于工具:如果不重新启动 mac 并从头开始,在我的 mac(5 Meg)上运行内存工具似乎非常困难。这真的很烦人!甚至在程序开始之前仪器就一直在崩溃,那么有替代解决方案吗?

【问题讨论】:

  • 使用 Instruments 分析您的应用。
  • 对象的保留计数在很大程度上是没有意义的,在这种情况下肯定是没有意义的。
  • 您的 doSomethingWithMyObj: 方法是否在做任何事情来获得对您的对象的强引用?
  • pre-ARC 可以覆盖“retain”以查看谁保留了您的对象。在我们新的 ARC 世界中,有没有办法做到这一点?
  • 您的 Mac 有 5 Meg?需要解释一下吗?

标签: iphone ios automatic-ref-counting


【解决方案1】:

获取对象的retainCount?

你可以只做一个断点并输入下面的命令来获取对象的retainCount

po object.retainCount

【讨论】:

    【解决方案2】:

    即使在 ARC 下,您也可以将 CFGetRetainCount 与 Objective-C 对象一起使用:

    NSLog(@"Retain count is %ld", CFGetRetainCount((__bridge CFTypeRef)myObject));
    

    虽然for reasons amply described elsewhere,这不是对调试特别有用。如果您需要了解对象在何处被保留和释放,请查看 this answer 以获取使用分配工具的帮助。

    我发现检查保留计数实际上有用的唯一情况是在 dealloc 方法中,当某些东西保留并自动释放正在释放的对象时。这将在稍后自动释放池耗尽时导致崩溃。您可以通过检查每条消息之前和之后的保留计数来查明原因。通过这种方式,我发现the observationInfo method(它本身通常只对调试有用)保留并自动释放self。然而,即使是这类问题通常也可以在不检查保留计数的情况下解决,只需将 dealloc 的整个主体包装在 @autoreleasepool 块中即可。

    但是,保留计数可用于了解某些类的实现。 (这样做只是为了娱乐或好奇!切勿依赖生产代码中未记录的实现细节!)

    例如,立即在main 中的@autoreleasepool 内尝试此操作:

    NSNumber *n0 = [[NSNumber alloc] initWithInt:0];
    NSLog(@"0 reference count = %ld", CFGetRetainCount((__bridge CFTypeRef)n0));
    // Prints 2 in my test
    

    所以NSNumber 可能会缓存(或至少重用)某些实例。但不是其他人:

    n0 = [[NSNumber alloc] initWithInt:200];
    NSLog(@"n0 reference count = %ld", CFGetRetainCount((__bridge CFTypeRef) n0));
    // Prints 1 - I am the sole owner of this instance.  There could be weak
    // or unretained references to it, but no other strong references.
    
    NSNumber *n1 = [[NSNumber alloc] initWithInt:200];
    NSLog(@"n1 reference count = %ld", CFGetRetainCount((__bridge CFTypeRef) n1));
    // Prints 1 again.  New instance with same value as prior instance.
    // You could of course compare pointers to see that they are separate
    // instances.
    

    如果你 alloc 但不初始化,你甚至会发现 NSNumber 返回一个单例:

    n1 = [NSNumber alloc];
    NSLog(@"n1 reference count = %ld", CFGetRetainCount((__bridge CFTypeRef) n1));
    // Prints -1.
    

    (请注意,您还可以通过查看 Core Foundation 源代码来了解有关 NSNumber 的许多详细信息,该源代码位于 http://opensource.apple.com。但谁知道如果您查看对象的保留计数会发现什么?与 Core Foundation 中的对象不是免费桥接的吗?)

    【讨论】:

    • 这很适合调试。
    • 不,不是。在 ARC 下,它比其他情况更具误导性(因为编译器将在优化级别、版本以及可能的调试与否之间移动保留/释放/自动释放调用)。
    • 哇。这是一个地狱般的编辑! +1!有趣的细节;您的NSNumber 示例的值为200 将由于实现细节(例如标记的指针)而改变不同平台和不同操作系统版本上的行为,进一步说明retainCount 的不可预测性。
    【解决方案3】:

    使用 Instruments 并通过在“对象列表”中搜索类名或指针地址来定位要跟踪的对象。

    找到它后,点击实例上的显示箭头。这会将您带到保留和释放的历史视图。

    如果您展开右侧详细视图,您还将看到每个保留/释放的调用堆栈。

    【讨论】:

    • 这很有帮助。 Stackoverflow 和其他地方关于如何使用 Instruments 调试保留周期的文档非常差。这个截图和简短的描述帮助我弄清楚了
    • 在我找到这个答案之前遇到了麻烦:stackoverflow.com/questions/29121260/…
    • @Saltymule 为什么投反对票?这似乎与答案没有直接关系,而是 Xcode 如何加载符号的一般问题
    • @HenrikHartz 据我所知,我没有投反对票。我引用的答案表明 8.2 和 8.3 没有正确加载符号信息。在我在 8.1 模拟器上尝试之前,我对这个答案有疑问。该评论旨在为遇到相同问题的未来读者提供面包屑。
    • @Saltymule 我的错,我愚蠢地认为最后一条评论是点击通知时的 d/v :) 感谢您为帖子增加价值!
    【解决方案4】:

    你没有。 ARC 为您处理内存管理,并且不允许您调用 retainCount,即使您可以看到它,它返回的数字对您来说也毫无意义。如果你愿意,你应该使用 Leaks and Allocations 工具在 Instruments 中进行内存分析。这是查看和查看您的应用程序如何分配内存并在其中发现任何不当使用内存的最佳方式。

    【讨论】:

    • 如果 Instruments 不断崩溃会怎样?
    • 然后你需要看看 Instruments 崩溃的原因,如果你的跟踪方式一次太多,或者你可能需要重新安装开发工具。如果 Instruments 一直在崩溃,这听起来像是我可能会提出另一个问题或(也许更好)去 Apple 开发者论坛。
    【解决方案5】:

    无论有无 ARC,您都不应该对任何事情使用 retainCount。

    When to use -retainCount?

    【讨论】:

      【解决方案6】:

      你没有。 Apple 说您不需要,因为 ARC 会为您处理。

      【讨论】:

      • 了解,但是特定限定符、外部非弧库会发生什么......
      • 您使用编译器标志 -fno-objc-arc 来不将 ARC 与它们的库一起使用。
      • 我知道如何使用 ARC 代码编译非 ARC lib,但我的问题是如何确保整个链中没有内存泄漏,并且对象具有预期的保留计数。跨度>
      • 我对 obj-c 还很陌生,但我很确定在 WWDC 介绍 ARC 的演讲中,他们说他们设计了 ARC,所以你根本不用担心保留计数。它只是工作。
      • “它只是工作”,直到您从块中引用self 或实例变量,从而导致捕获,从而经常导致保留周期。编译器可以警告您其中一些情况,但不是全部。 (这不是 ARC 无法解决的唯一潜在问题。错误的桥接转换是另一个问题。编译器仍然无法读懂您的想法。)仍然需要 Instruments 的内存分析工具。
      【解决方案7】:

      我认为唯一的方法是使用分配工具来分析您的应用程序。您需要单击信息描述符(左窗格中分配旁边的“i”),然后单击“记录引用计数”。然后,您可以分析您的应用程序并搜索您要检查的特定类。从那里,您可以在“扩展详细信息”窗格中找到该类的每个实例的保留计数。

      您也可以使用 Leaks 来执行此操作(因为我相信它是 Allocations 工具的一种变体)。

      【讨论】:

      • 几乎;您使用分配工具分析您的应用程序,根本不需要记录引用计数。如果内存在增长,那么您可以使用诸如堆分析和/或记录引用计数之类的工具来追踪内存增长。
      • 好吧,如果他想检查内存泄漏(这可能是真的),但他问如何验证保留计数,我相信这是唯一的方法。
      • 验证保留计数是一件绝对没用的事情。完全浪费时间,在写下你的答案时指出这一点是完全公平的。
      • 您不能假设保留计数。这些框架可以——而且确实——经常在你背后保留一些东西,有时以令人惊讶的方式。看retainCount 是浪费时间。如果你有记忆的积累,你必须知道保留的回溯才能让它有意义。保留计数本身对于调试目的完全没有用,应该删除 retainCount 方法。
      • 我实际上没有遇到 Instruments 崩溃的问题,所以我广泛使用它。如果崩溃的不是“Instruments”,而是您的应用在 Instruments 中崩溃,这可能是由于一个更微妙的错误。 Instruments 通常使用发布版本,而调试使用调试版本。使用静态分析器也有帮助:产品 -> 分析。除此之外,我搜索代码以查找泄漏...但我确实非常依赖仪器。当我怀疑一个保留周期时,我会检查保留计数,主要是为了确定是否有多个周期。
      猜你喜欢
      • 2019-05-25
      • 2015-09-29
      • 1970-01-01
      • 2010-09-07
      • 2017-04-09
      • 1970-01-01
      • 2018-04-11
      • 1970-01-01
      • 2020-10-19
      相关资源
      最近更新 更多