有没有一种方法可以“查找神秘保留”...?

34

最近我在修复一个人的代码。有一个大类无法释放。你需要用5到6个releases才能让它dealloc。

我仔细查看了这个大类,最终找到了需要释放的各种东西。

这让我想到:一定有一种非常简单的方法可以“找到”对象上的所有retains……对吗?

那么,有没有简单的方法“找到对象上的所有retains”?XCode或Instruments中是否有每个人都知道的按钮?

如果找不到像这样的神秘保留,你会怎么做?

所以,在iOS世界中,如果有人知道“显示此对象所有保留来源”的按钮——谢谢!

P.S.请注意,这里没有泄漏,这个问题与泄漏完全无关。该对象只是“完全正确”地不会释放。


后来……

Fabio提供的真正惊人的解决方案:

Fabio为这个问题提供了一个惊人的解决方案。 说出九个单词就行了:

-(id)retain
    {
    NSLog(@"%@", [NSThread callStackSymbols]);
    return ([super retain]);
    }

这在很多情况下都非常有用,并且会引导出许多其他有用的东西。Fabio,你可能每年会永远为我节省两个人周的工作时间。谢谢!

顺便说一句,如果你刚刚开始学习并且对输出感到困惑,我发现通常会有许多包含“UINib instantiateWithOwner:”的块。看起来这些将首先出现,然后是重要的块。


2
当然,CADisplayLink 会保留对象。文档也是这么说的:“新构建的显示链接会保留目标。” 要让保留消失,您需要使 CADisplayLink 失效。 - Dave DeLong
有趣的、非平凡的问题 (+1)。最好的是第一句话;-) - GorillaPatch
1
这在ARC下不起作用。你不能调用[super retain]。 - Greg Maletic
在 ARC 环境中,您需要首先将 -fno-objc-arc 添加到编译器标志中,以允许您覆盖保留和调用 super。 - Fattie
6个回答

72

工具可以展示任意 Obj-C 对象的每个 malloc、release 和 retain 的调用栈,而且无需更改任何代码即可实现。它适用于使用 ARC 的情况,而 fabio 提供的解决方案则不是。

这对于查找神秘的引用非常有用,例如当一个对象应该被释放但却没有被释放时。

下面是具体操作:

  • CMD + I (Product / Profile)
  • 在 Instruments 弹出窗口中选择 'Allocations'(不是 'Leaks')
  • 应用程序应该开始运行
  • 进行任何导致出现神秘的引用的操作
  • 在左侧面板中选择 'Allocation' 工具
  • 按下 CMD + 1 或选择右侧带有波形的圆圈。在右下角的面板中,选中 'Record reference counts' 选项。这很重要,否则只会记录 malloc 和 free。
  • 在列表的右上角搜索框中,输入类名(例如 BCMyObject)
  • 这将过滤 'Statistics' 列表,以显示当前有多少个类的实例处于活动状态。#Persistent 列显示了当前活动的实例数。
  • 点击该行,然后点击类名旁边的小箭头->。你将看到面包屑显示 'Statistics > Allocation Summary > BCMyobject'
  • 这将展示所有该类的实例(以及哪些实例处于活动状态)
  • 选择一个实例,并再次点击箭头(这次是地址旁边的箭头)
  • 现在你将看到面包屑中显示 'Statistics > Allocation Summary > BCMyObject > History: 0xADDRESS'
  • 这会列出对象每次被 malloc、retain 或 release 的情况
  • 现在,在左侧面板中,单击 'Record Reference Counts' 选项旁边的外观像条形图一样的图标或按下 CMD + 3
  • 选择其中一行,您将看到导致调用的完整调用堆栈
  • 很容易!(有点)


    3
    没错,这就是正确的答案。覆盖掉 "retain" 也是一种解决方法(虽然有点"hacky"),但它在ARC下不起作用,而这个工具可以。 - Gobra
    1
    谢谢你的回答 - 我知道你可以使用Leaks工具自动获取这些信息,但我无法弄清如何使用allocations工具获取它,因为默认情况下它只显示mallocs。 - Luke Redpath
    1
    您先生,您拯救了我的生命,我现在对您为保护我的理智所做的贡献感激不尽。 - Jason
    @BenClayton 我想知道的是,为什么我们不能使用Leaks工具来检测那些泄漏,而不是检查分配情况?Instruments出现故障了吗?编辑:我正在尝试过滤掉在应用程序结束时不会释放的实例。检查分配情况是一种选择,但它们在Leaks工具中不显示为泄漏。 - mostruash
    1
    @mostruash,应用程序结束时不会出现泄漏的情况。如果应用程序关闭,则由其分配的所有内容都将被释放。无论它之前是否被释放。 - weezor
    一个请求:你可以更新一下这些针对Xcode 6.3及更高版本的指导吗?Instruments用户界面已经发生了很大变化。非常感谢。 - Greg Maletic

    22

    只是猜测...但您可能会覆盖自定义类的保留方法,调用super并抛出一个漂亮的NSLog来打印调用堆栈。


    使用Joe的实际代码进行更新

    -(id) retain {
    NSLog(@"%@", [NSThread callStackSymbols]);
    return ([super retain]);
    }
    

    另一个重要的细节是 [NSThread callStackSymbols] 返回一个 NSString 数组,可以进行过滤并用于其他目的。例如,在复杂和动态代码中,可以检查一个方法是否正确地导致另一个方法被触发。

    注意: 在 ARC 环境中,您需要首先将 -fno-objc-arc 添加到编译器标志中,以允许您覆盖保留和调用 super。


    @Joe callStackSymbols 是我在 Stack Overflow 上发现的一个不错的技巧(我想)。不幸的是,我无法找到原始答案。很高兴你喜欢这个解决方案!!! - Fabio
    这在 ARC 下不起作用。你不能调用 [super retain]。 - Greg Maletic
    1
    太棒了,Greg,谢谢 - 我在想使用 ARC 后的“等效”是什么?或许只需返回 super。 - Fattie
    你也不能在 ARC 中覆盖 retain。 - wfbarksdale
    这个解决方案在今天已经不再适用了,因为现在大家都使用 ARC,如果禁用 ARC 编译,你会得到大量其他的警告信息。 - Nikita
    显示剩余2条评论

    5

    在自定义类的retain方法上设置断点

    你可以在retain方法上设置一个符号断点,然后将其设置为自定义类的retain方法。这里的问题是retain是NSObject类的一个方法,因此在设置断点时会出现所有Objective-C类的选择。

    在这种情况下,最好的方法是覆盖自定义类的retain方法,并调用super方法,这样它就不会执行任何操作,但是您可以在其中设置断点。

    使用断点操作记录调用者

    要添加断点操作,请双击蓝色标记。在列表中找到断点并单击右侧的+按钮。然后选择Debugger command并在此字段中添加GDB命令frame 1,它将显示retain的调用者。通过这种方式,您可以记录所有的retains及其来源。类似地,记录releases也可以检查多余的release。

    虽然有些繁琐,但这是我能想到的最好的方法。


    嗯,我不知道这是否是一个常见的问题。有时候可以通过一些Objective-C运行时的魔法来找出一些信息,但我无法看出如何追踪函数调用。 - GorillaPatch
    我曾经在好奇自己的对象是由谁保留时这样做过。我创建了一个自定义子类,重写了-retain方法,并让它调用[super retain]。如果在调用super时设置断点,每次触发时查看堆栈跟踪很容易就能知道是谁负责的。对于更频繁的保留/释放事件,您甚至可以使用音频断点,在使用应用程序时听到这些事件。 - Brad Larson

    1

    很遗憾,编程上无法轻松地确定一个对象的“所有权”,因为“对象所有权”的概念是一种编码约定(除非您启用垃圾回收)。

    堆栈日志记录通常很有用(我通常使用几个断点和bt;continue),但这只告诉您调用保留的函数,而不是“更大的图像”(例如,您可能会使用[ivar2 release]; ivar2 = ivar1; ivar1 = nil;“转移所有权”)。有时它是UIKit泄漏,所以您没有源代码,您真的必须深入挖掘。

    如果这不是泄漏,请多次调用-release并查看它在哪里崩溃!


    1

    仪器及其内存管理是你的好朋友。泄漏和僵尸是最有价值的工具之一。请使用它们。

    产品 -> 配置文件(或Cmd-I)


    @Joe,学习如何使用Instruments有很多资源可供参考。首先可以找到WWDC视频。至于覆盖-retain,这样做可能有效,但感觉不太对。 - Dave DeLong
    所以,如果您让这个对象泄漏,Instruments 将显示对象的所有保留。 - AechoLiu
    1
    这个问题与泄漏或Leak工具完全没有关联。干杯! - Fattie

    -1
    你尝试过在Xcode中使用“构建和分析”吗?
    它非常适合查找未被释放的对象。

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接