了解一个malloc_history转储

如果您曾经问​​过如何在objective-c中debugging释放/分配问题,您将遇到以下环境设置,可以帮助跟踪问题:

  • NSZombieEnabled – 在发布后保持不变,所以你可以得到指针等。
  • MallocStackLogging – 保留对象历史logging以备日后参考
  • NSDebugEnabled

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


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

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

然后打开terminal,而debugging器已转发中断并input:

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那么为什么错误表明它被释放(net + 1 alloc)?

或者是我的转储错误的理解?


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

仪器僵尸检测:

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

看着僵尸的回溯,看起来像保留计数被调用:Quartz through release_root_if_unused


编辑: 解决 – 我是从超级删除视图,然后释放它。 通过释放它来修复。

@凯是正确的; malloc历史显示在指定地址的两个分配; 一个已经分配和释放,一个还在玩。

您需要的是已经发布的CALayer上的retainCount调用的回溯。 因为你已经启用了僵尸检测,所以在其他的内存debugging中,可能是因为释放根本就不会发生。

将malloc历史与僵尸检测混合在一起会显着改变运行时间的行为。

我build议运行在仪器中的僵尸检测。 希望能指出确切的问题。

如果没有,那么当僵尸被发送消息时,你可以设置一个断点。 设置断点,看看你停在哪里。


好的 – 所以,CoreAnimation使用保留计数来实现内部目的(系统框架可以逃脱这一点,尽pipe它是脆弱的)。

我认为-1是红鲱鱼; 僵尸可能会返回0xFF …. FFFF作为保留计数,并在仪器中呈现为-1。

下一个最好的猜测 因为这是发生在一个计时器,过度释放可能发生在animation。 CoreAnimation图层应该正确处理。 在代码中有一个视图或animation层容器的过度释放,导致图层过早消失。

你有没有尝试过“构build和分析”? 这可能会导致某个地方的pipe理不善。

无论如何,作为一个实验,尽量保留你的观点多一点时间,看看是否使这个问题停止。 如果是这样,那至less是一个线索。

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

最后, 谁在调用retainCount ?!?!? 在CoreAnimation的情况下, retainCount可能在内部被用作实现细节。

如果这是你的代码,那么僵尸电话的位置应该是非常明显的。

我不是专家,但是如果你看一下第三部分的第一行:

ALLOC 0x4dbb170 -0x4dbb19f [size = 48 ]:

而在另外两个输出中,0x4dbb160-0x4dbb171的大小为18的内存块被分配并释放。 我假设旧的对象被释放,并有一个新的对象驻留在这个内存地址。 因此,0x … b160的旧实例不再有效。