【问题标题】:NSOpenPanel not showing files after visiting folder for the first time第一次访问文件夹后,NSOpenPanel 不显示文件
【发布时间】:2016-01-28 22:06:33
【问题描述】:

经过几天的调试,我仍然无法弄清楚为什么 NSOpenPanel(或 NSSavePanel,就此而言)在第一次访问文件夹后会显示一个空文件列表。它这样做似乎是随机的,即有时会显示文件(可能是因为它们被缓存),但看起来,如果第一次访问文件夹或卷,则不会。如果用户返回并再次访问同一个文件夹,所有文件都会显示出来。

NSOpenPanel 根据需要在主线程上模态运行。有另一个线程在运行。降低其他线程的优先级并没有帮助。暂停它不是一种选择。

我怀疑 OS X 在完成文件检索后会向面板(或应用程序)发送一些延迟事件(这可能需要一段时间)。无论出于何种原因,我的 NSOpenPanel 似乎都错过了它。

那是什么活动?我怎样才能防止它丢失?

编辑:NSOpenPanel 由委托对象在主线程上构建和打开。面板从带有[delegate performSelectorOnMainThread: @selector(runPanel) withObject: nil waitUntilDone: NO] 的辅助线程打开。所以辅助线程会立即继续工作并轮询委托,直到面板完成,以获取结果。

(是这样的,因为辅助线程运行的模拟需要在用户提供输入的同时继续进行)

编辑:NSOpenPanel 有一个附属视图。

编辑:面板运行时在主线程上堆栈帧。

-[SavePanelDelegate runPanel] + 185
-[NSObject performSelector:withObject:] + 70
__NSThreadPerformPerform + 318
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
__CFRunLoopDoSources0 + 235
__CFRunLoopRun + 1022
CFRunLoopRunSpecific + 394
CFRunLoopRunInMode + 123
RunCurrentEventLoopInMode + 259
ReceiveNextEventCommon + 526
_BlockUntilNextEventMatchingListInModeWithFilter + 92
_DPSNextEvent + 1602
-[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 119
-[NSApplication run] + 727
NSApplicationMain + 1165
main + 99

今天我在重现问题时遇到了困难。直到一个小时左右并重新启动 Mac 后,它才再次发生。很奇怪。

【问题讨论】:

  • NSLog(@"%@", [NSThread callStackSymbols]); 放在-runPanel 方法的开头附近并显示输出。另外,当这种情况发生时,还有什么其他记录到控制台吗?不妨也显示您的 -runPanel 方法的代码。
  • 堆栈框架或控制台上没有任何可疑之处。看到它已添加到帖子中。
  • 更改文件选择后,有一个临时线程TThumbnailExtractorThreadTConditionVariable::WaitWithTimeout() 相关。它可能正在收集要显示的文件图标。一段时间后,线程消失了。由于没有记录错误,因此很难评估这是否与此处相关。

标签: objective-c macos cocoa nsopenpanel


【解决方案1】:

您是否正在从提交到主调度队列 (dispatch_get_main_queue()) 的任务中运行打开面板?请记住,代码路径可能是间接的。

如果是这样,那可能就是问题所在。

主调度队列是串行的。它一次只能运行一个任务。从这样的任务模态运行打开面板意味着在打开面板和当前任务的其余部分完成之前不会启动其他任务。 Apple 以其无限的智慧使在打开面板中呈现文件的某些方面依赖于提交到主调度队列的任务。在您的情况下,这些任务在面板关闭并且不再需要它们的结果之后才会运行。

当这发生在我身上时,等待这些结果似乎有一些超时。大约一分钟后,文件出现了,但带有通用图标。

为避免此问题,您需要使用-[NSObject performSelectorOnMainThread:...]CFRunLoopPerformBlock()。这两个都是基于主运行循环,它不是串行的。它是可重入的。有关详细信息,请参阅this answer

【讨论】:

  • 谢谢。我在帖子中添加了一些背景信息。不涉及使用dispatch_get_main_queue()。辅助线程是用detachDrawingThread:toTarget:withObject: 创建的,并从那里用performSelectorOnMainThread: 调用面板(通过管理它的委托对象)。
  • 我现在在没有启动模拟的情况下在第二个线程上测试了代码,它运行良好。所以看起来这个问题与我的 Cocoa 代码无关,而是与第二个线程上的模拟有关,它可能会干扰 I/O,从而使面板错过某些东西。
  • 您可以尝试在无法在面板中填写文件时对过程进行采样。使用sample 命令行工具或活动监视器。在您的问题中发布示例报告。
猜你喜欢
  • 1970-01-01
  • 2011-08-06
  • 1970-01-01
  • 2011-02-07
  • 1970-01-01
  • 1970-01-01
  • 2019-08-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多