NSTableView reloadData 内存泄漏

Posted

技术标签:

【中文标题】NSTableView reloadData 内存泄漏【英文标题】:NSTableView reloadData leaking memory 【发布时间】:2012-08-08 23:30:30 【问题描述】:

我一直在使用 Instruments 应用程序检查我的应用程序是否存在泄漏。在某些情况下,HUD 面板中的表格视图每秒更新一次。除了每秒(reloadData) _NSArrayl 对象的数量增加一之外,一切正常。如果我将数据源更改为 return(@""); (它们都是文本单元格)然后问题仍然存在(没有变化)。如果我将所有周围的代码删除为:

[myTableView reloadData];

- (id)tableView:(NSTableView *)tv objectValueForTableColumn:(NSTableColumn *)column row:(int)rowIndex 
   return(@"");

(甚至返回 nil)

问题仍然存在。注释掉 [myTableView reloadData],问题就消失了。 每个块是一个_NSarrayl,大小为32字节,详细:

   0 libsystem_c.dylib calloc
   1 libobjc.A.dylib class_createInstance
   2 CoreFoundation __CFAllocateObject2
   3 CoreFoundation +[__NSArrayI __new::]
   4 CoreFoundation -[__NSPlaceholderArray initWithObjects:count:]
   5 CoreFoundation +[NSArray arrayWithObjects:]
   6 AppKit -[NSWindow _runLoopModesForInvalidCursorRectsObserver]
   7 AppKit __-[NSWindow _postInvalidCursorRects]_block_invoke_1
   8 CoreFoundation __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
   9 CoreFoundation __CFRunLoopDoObservers
  10 CoreFoundation __CFRunLoopRun
  11 CoreFoundation CFRunLoopRunSpecific
  12 HIToolbox RunCurrentEventLoopInMode
  13 HIToolbox ReceiveNextEventCommon
  14 HIToolbox BlockUntilNextEventMatchingListInMode
  15 AppKit _DPSNextEvent
  16 AppKit -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:]
  17 AppKit -[NSApplication run]
  18 AppKit NSApplicationMain

我注意到这些对象的历史记录中有一个 Autorelease 条目,所以我想知道它是否已被自动释放(保留计数仍为 1)。但是 5 分钟后,没有任何东西释放它们,所以可能自动释放池没有被释放。我不创建任何自动释放池(也不释放任何),而是留下应用程序框架。我应该对自动释放池做些什么吗?

#   Address Category    Event Type  RefCt   Timestamp   Size    Responsible Library Responsible Caller
0   0x100669cf0 __NSArrayI  Malloc  1   00:01.342.634   32  AppKit  -[NSWindow _runLoopModesForInvalidCursorRectsObserver]
1   0x100669cf0 __NSArrayI  Autorelease <null>  00:01.342.636   0   AppKit  -[NSWindow _runLoopModesForInvalidCursorRectsObserver]

非常感谢任何关于我可能做错了什么的建议。

请注意,1秒更新的情况对于应用程序来说并不是“正常情况”,但它可能会发生,然后应用程序设计了很长时间;即不是您运行、退出、再次运行、退出等的应用程序。

它们没有被检测为“泄漏”,但分配计数一直在增加,所以我认为它们实际上是泄漏(或出现问题)

我没有使用 ARC 而不是“垃圾收集” - 只是保留/释放系统,我倾向于使用保留/释放,而我自己的代码只是偶尔使用自动释放,而且与此代码无关。

附加信息:我一直在看着计数增加,只使用 Instruments 应用程序(表格视图位于始终可见的 HUD 中)。但是,如果我例如将鼠标移到“泄漏”的 tableview 上 - 计数返回到合理的值。在屏幕上稍微移动包含 tableview 的 HUD 窗口,计数会回落,等等。似乎是缺少用户活动导致这种情况发生。我仍然很困惑,但我认为这个新的观察结果可能对想法有所帮助(或者如果有人试图重现它)。

【问题讨论】:

【参考方案1】:

代码本身不会泄漏。但是可能发生的是外部自动释放池没有耗尽。这在 AppKit 中是一件棘手的事情:AppKit 运行循环会自动添加一个自动释放池,但只有在它接收到事件(即您移动鼠标)时才会耗尽它。

您可以通过激活应用程序并移动鼠标然后再次尝试泄漏来确认。

这意味着,一般来说,在您的自定义非事件驱动回调中添加您自己的自动释放池是一个好主意。一些基本的 Foundation 机制已经为您完成了这项工作(例如,NSTimer 在触发之前为您创建了一个池并在之后将其排出),但并非所有内容都为您完成。

在这种特殊情况下,您似乎对该特定​​回调没有太多控制权,因此:

1) 考虑在运行泄漏之前对您的应用程序进行处理

2) 可能向 Apple 提交错误

【讨论】:

对不起 - 我搞砸了并发布了一个后续问题作为答案,它出现在您的答案上方。基本上,按照你的建议,我尝试了虚拟事件......工作......不确定......请参阅上面的详细信息。非常感谢【参考方案2】:

更新和后续问题(抱歉)** 根据您的建议,我在 [myTableView reloadData] 下方添加了一些内容,以向应用程序发布虚拟/无害消息(看起来比直接发布到相关窗口更安全)。

NSEvent *dummyEvent=[NSEvent mouseEventWithType:NSMouseMoved location:NSMakePoint(0.0,0.0) modifierFlags:0 timestamp:0.0 windowNumber:0 context:nil eventNumber:0 clickCount:0 pressure:0.0];
[NSApp postEvent:dummyEvent atStart:FALSE];

我的想法是添加一些在表格视图完成所有内容后会发生的事情(基本上是当事件队列变空时。这看起来就像用户在做某事。它似乎在不断增加的分配计数中起作用不再增加(按预期稍微移动但基本稳定)。

但是,我对事件的处理并不多,我担心我的“dummyEvent”可能不是无害的,或者将来可能会开始引起问题。有没有更好的方法来实现这一点,或者我应该使用更好的事件(或参数)吗?

我试过

NSEvent *dummyEvent=[NSEvent otherEventWithType:NSApplicationDefined location:NSMakePoint(0.0,0.0) modifierFlags:0 timestamp:0.0 windowNumber:0 context:nil subtype:(short)(0) data1:0 data2:0];

但我对此不太信任,因为我没有自定义事件处理程序,并且不确定它最终会在哪里结束。另外,我第一次使用这个变体也遇到了地址违规 - 但我想知道这是否是其他事情,因为它没有再次发生。只要我的参数都不会导致崩溃/断言失败或类似情况,我可以看到虚拟鼠标移动是如何无害的。

【讨论】:

如果您知道您可能会在应用程序处于后台时触发回调,并且您不能让它们使用自己的自动释放池,那么这确实是要做的事情:tickle AppKit 的事件循环。而你的第二个猜测是正确的(这里不要乱用鼠标事件)。 非常感谢您在这方面的建议和帮助。非常感谢。第二个选项是(NSApplicationDefind)。 TableView 更新是从一个定时器事件开始的;计时器调用对象方法,该方法调用其他一些东西,然后调用 tableview reloadData。因此,从您所说的以及 Instruments 显示的内容来看,它表明显示更新与计时器事件处理程序是异步的,或者至少在计时器事件自动释放池之外。

以上是关于NSTableView reloadData 内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

NSTableView reloadData 外部类

NSTableView reloadData - 索引超出范围异常

调用 reloadData 时自定义 UITableViewCell 内存泄漏

.reloadData() 没有重新创建可见单元格或内存中的单元格?

拖出 NSTableView 以移除

NSTableView:创建“重叠”行