9

如果您曾经问​​过我如何在 Objective-c 中调试发布/分配问题,您会遇到这些有助于追踪问题的环境设置:

  • NSZombieEnabled- 发布后保留下流,所以你可以得到指针等。
  • MallocStackLogging- 保留对象历史以供以后参考
  • NSDebugEnabled

您将所有这些设置为YES在“可执行文件”(在组树中找到)信息的“参数”选项卡的“环境”部分中。


所以,我得到这个控制台输出

MyApp [ 4413 :40b]-[CALayer retainCount]:消息发送到已释放实例0x4dbb170

然后打开终端,而调试器已转发中断并键入:

malloc_history 4413 0x4dbb170

然后,我得到一个大文本转储,据我了解,重要的是:

1

ALLOC 0x4dbb160-0x4dbb171 [size=18]:
thread_a0375540 |start | main |
UIApplicationMain | GSEventRun |
GSEventRunModal | CFRunLoopRunInMode |
CFRunLoopRunSpecific | __CFRunLoopRun
| __CFRunLoopDoTimer |
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
| __NSFireDelayedPerform |
-[todoListViewController drillDocumentMenu:] |
-[documentListViewController drillIntoDocumentWithToDoRecord:] |
-[documentViewController OpenTodoDocument:OfType:WithPath:] |
-[documentViewController OpenDocumentOfType:WithPath:] |
-[documentViewController managePDFDocumentWithPath:] |
-[PDFDocument loadPDFDocumentWithPath:andTitle:] |
-[PDFDocument getMetaData] | CGPDFDictionaryApplyFunction |
ListDictionaryObjects(char const*,
CGPDFObject*, void*) | NSLog | NSLogv
| _CFLogvEx | __CFLogCString |
asl_send | _asl_send_level_message |
asl_set_query | strdup | malloc |
malloc_zone_malloc 

2

FREE  0x4dbb160-0x4dbb171 [size=18]:
thread_a0375540 |start | main |
UIApplicationMain | GSEventRun |
GSEventRunModal | CFRunLoopRunInMode |
CFRunLoopRunSpecific | __CFRunLoopRun
| __CFRunLoopDoTimer |
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
| __NSFireDelayedPerform |
-[todoListViewController drillDocumentMenu:] |
-[documentListViewController drillIntoDocumentWithToDoRecord:] |
-[documentViewController OpenTodoDocument:OfType:WithPath:] |
-[documentViewController OpenDocumentOfType:WithPath:] |
-[documentViewController managePDFDocumentWithPath:] |
-[PDFDocument loadPDFDocumentWithPath:andTitle:] |
-[PDFDocument getMetaData] | CGPDFDictionaryApplyFunction |
ListDictionaryObjects(char const*,
CGPDFObject*, void*) | NSLog | NSLogv
| _CFLogvEx | __CFLogCString |
asl_send | _asl_send_level_message |
asl_free | free

3

ALLOC 0x4dbb170-0x4dbb19f [size=48]:
thread_a0375540 |start | main |
UIApplicationMain | GSEventRun |
GSEventRunModal | CFRunLoopRunInMode |
CFRunLoopRunSpecific | __CFRunLoopRun
| __CFRunLoopDoTimer |
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
| __NSFireDelayedPerform |
-[todoListViewController drillDocumentMenu:] |
-[documentListViewController drillIntoDocumentWithToDoRecord:] |
-[documentViewController OpenTodoDocument:OfType:WithPath:] |
-[documentViewController OpenDocumentOfType:WithPath:] |
-[documentViewController managePDFDocumentWithPath:] |
-[ScrollViewWithPagingViewController init] | -[UIView init] |
-[UIScrollView initWithFrame:] | -[UIView initWithFrame:] | UIViewCommonInitWithFrame | -[UIView
_createLayerWithFrame:] | +[NSObject(NSObject) alloc] | +[NSObject(NSObject) allocWithZone:] | class_createInstance |
_internal_class_createInstanceFromZone | calloc | malloc_zone_calloc

我不明白的是,如果它的历史是 ALLOC、FREE、ALLOC 那么为什么错误表明它已被释放(净 +1 分配)?

还是我对转储的理解是错误的?


编辑(新运行=不同的对象指针):

用仪器检测僵尸:

为什么以及如何,保留计数从 1 跳到 -1?

查看 Zombie 的回溯,看起来保留计数被调用:Quartz through release_root_if_unused


编辑:已解决-我正在从 super 中删除一个视图,然后将其释放。只需释放它即可修复。

4

2 回答 2

2

@Kay 是正确的;malloc 历史显示指定地址的两个分配;一个已被分配和释放,一个仍在使用中。

您需要的是对已发布的调用的retainCount回溯CALayer。因为您启用了僵尸检测,除了其他内存调试之外,可能是释放根本没有并且不会发生。

将 malloc 历史记录与僵尸检测相结合会显着改变运行时行为。

我建议在 Instruments 中运行僵尸检测。希望这将查明确切的问题。

如果没有,那么有一个断点可以设置为在收到僵尸消息时中断。设置该断点并查看您停止的位置。


好的——所以,CoreAnimation 将保留计数用于内部目的(系统框架可以摆脱这一点,尽管它很脆弱)。

我认为 -1 是红鲱鱼;僵尸很可能返回 0xFF....FFFF 作为保留计数,这在 Instruments 中呈现为 -1。

下一个最佳猜测;由于这是在计时器中发生的,因此过度释放可能发生在动画期间。CoreAnimation 层应该正确处理这个问题。代码中的视图或动画层容器过度释放,导致该层过早消失。

您是否尝试过“构建和分析”?偶然它可能会在某个地方发现对视图的管理不善。

在任何情况下,作为一个实验,试着多保​​留你的观点一段时间,看看这是否能让这个问题停止。如果是这样,那至少是一个线索。

(或者它可能是系统框架中的一个错误......也许......但值得怀疑。)

最后,到底是谁在打电话retainCount?!?!? 在 CoreAnimation 的情况下,它可能在retainCount内部用作实现细节。

但是,如果它是您的代码,那么僵尸调用的位置应该非常明显。

于 2010-12-06T16:26:29.797 回答
0

我不是专家,但是如果您看一下第 3 块中的第一行:

分配 0x4dbb170 -0x4dbb19f [大小= 48 ]:

而在其他两个输出中,0x4dbb160-0x4dbb171 处的大小为 18 的内存块被分配和释放。我假设旧对象已被释放,并且有一个新对象驻留在此内存地址。因此,位于 0x...b160 的旧实例不再有效。

于 2010-12-06T13:36:22.177 回答