Macos Cocoa/OSX-NSSavePanel中的奇怪行为,不显示子项

Macos Cocoa/OSX-NSSavePanel中的奇怪行为,不显示子项,macos,directory,nssavepanel,Macos,Directory,Nssavepanel,我有一个NSSavePanel实例,它有一个奇怪的行为:每当我打开它并单击目录的箭头(小展开按钮)时,它就会在左下角显示一个永不结束的不确定加载图标,并且不会显示目录/文件树。图像可以如下所示: 在那个例子中,我点击了“工作区”目录。面板上没有显示子项。更奇怪的是,在我再次单击它(重新绘制目录)然后再次单击(重新打开目录)之后,它正确地显示了所有文件 我的代码如下: // here, I'm creating a web service client, and then calling a m

我有一个NSSavePanel实例,它有一个奇怪的行为:每当我打开它并单击目录的箭头(小展开按钮)时,它就会在左下角显示一个永不结束的不确定加载图标,并且不会显示目录/文件树。图像可以如下所示:

在那个例子中,我点击了“工作区”目录。面板上没有显示子项。更奇怪的是,在我再次单击它(重新绘制目录)然后再次单击(重新打开目录)之后,它正确地显示了所有文件

我的代码如下:

// here, I'm creating a web service client, and then calling a method to download a report, and passing the same class as delegate
- (IBAction) generateReport:(id)sender {

    // SOME STUFF HERE...

    WSClient *client = [[[WSClient alloc] init] initWithDelegate:self];
    [client GenerateReport:@"REPORT" withParams:params];
}

- (void) GenerateReport:(NSString *)from withParams:(NSDictionary *)parameters {

    // SOME STUFF HERE...

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
    if (!error) {
        dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
            dispatch_async(dispatch_get_main_queue(), ^(void) {
                NSLog(@"GenerateReport: [success]");
                [self.delegate successHandlerCallback:data from: from];
            });
        });
    }
}];

// this is the callback
- (void) successHandlerCallback:(NSData *) data from: (NSString *) from {
    NSString savePath = [savePath stringByReplacingOccurrencesOfString:@"file://" withString:@""];
    NSString *filePath = [NSString stringWithFormat:@"%@", savePath];
    [data writeToFile:filePath atomically:YES];
}

// and this is to build a panel to let user chose the directory to save the file
- (NSURL *) getDirectoryPath {
    NSSavePanel *panel = [NSSavePanel savePanel];
    [panel setNameFieldStringValue:[self getDefaultFileName]];
    [panel setDirectoryURL:[NSURL fileURLWithPath:[[NSString alloc] initWithFormat:@"%@%@%@", @"/Users/", NSUserName(), @"/Downloads"]]];
    if ([panel runModal] != NSFileHandlingPanelOKButton) return nil;
    return [panel URL];
}
有人能暗示一下我失踪的地方吗

更新:对我来说,它似乎与dispatch\u async有关


提前谢谢

我刚刚找到了方法:我在主线程队列的dispatch\u async中进行所有调用。由于下载在回调时刻仍在运行,它与打开面板的线程冲突。我通过放置正确的行修复了所有问题,更改为:

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
    dispatch_async(dispatch_get_main_queue(), ^(void) {
        NSLog(@"GenerateReport: [success]");
        [self.delegate successHandlerCallback:data from: from];
    });
});
致:


在回调中,只是更新字段。最后,我发现所有这些都是我误解的主/后台线程。

事实上,这是
NSSavePanel
与Grand Central Dispatch主队列和/或
+[NSOperationQueue mainQueue]
之间的不良交互。您可以使用以下代码复制它:

dispatch_async(dispatch_get_main_queue(), ^{
    [[NSSavePanel savePanel] runModal];
});
首先,无论何时执行GUI操作,都应该在主线程上执行。(很少有例外,但现在应该忽略它们。)因此,如果要执行类似于打开文件对话框的操作,那么您将工作提交到主队列是正确的

不幸的是,主队列是串行队列,这意味着它一次只能运行一个任务,
NSSavePanel
将自己的一些工作提交给主队列。因此,如果将任务提交到主队列,并且该任务以模式方式运行“保存”面板,则它将独占主队列,直到“保存”面板完成。但是save面板依赖于将自己的任务提交到主队列并让它们运行的能力

就我而言,这是可可中的一种虫子。你应该向苹果提交一份bug报告

正确的解决方案是使用诸如运行循环源、
-performselectornmainthread:…
CFRunLoopPerformBlock()
之类的重入机制将其拥有的任何任务提交给主线程。它需要避免使用GCD的主队列或
NSOperationQueue


既然你迫不及待地想让苹果来解决这个问题,那么解决办法就是让你也这么做。使用上述机制之一将可能运行“保存”面板的任务提交到主队列。

Ken,您刚才提供了有关该问题的更详细解释。我接受你的回答作为对这个问题的最后回答。非常感谢。
dispatch_async(dispatch_get_main_queue(), ^{
    [[NSSavePanel savePanel] runModal];
});