NSOpenPanel第一次访问文件夹后没有显示文件

时间:2016-01-28 22:06:34

标签: objective-c macos cocoa nsopenpanel

经过几天的调试,我仍然无法弄清楚为什么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之后,它再次重新出现之前一直没有。怪异。

1 个答案:

答案 0 :(得分:2)

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

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

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

当我遇到这种情况时,似乎等待这些结果有一些超时。大约一分钟后,文件出现了,但是有了通用图标。

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