Objective c Cocoa:处理线程结果,并将多张工作表排队

Objective c Cocoa:处理线程结果,并将多张工作表排队,objective-c,multithreading,cocoa,cocoa-sheet,Objective C,Multithreading,Cocoa,Cocoa Sheet,我有一个多线程应用程序,它有许多并发操作同时进行。当每个线程完成时,它调用主线程上的两个方法之一 performSelectorOnMainThread:@selector(operationDidFinish:) // and performSelectorOnMainThread:@selector(operationDidFail:withMessage:) 当一个操作失败时,我启动一个显示错误消息的工作表,并向用户显示两个按钮,“取消”和“重试”。以下是我用来启动工作表的代码: //

我有一个多线程应用程序,它有许多并发操作同时进行。当每个线程完成时,它调用主线程上的两个方法之一

performSelectorOnMainThread:@selector(operationDidFinish:)
// and
performSelectorOnMainThread:@selector(operationDidFail:withMessage:)
当一个操作失败时,我启动一个显示错误消息的工作表,并向用户显示两个按钮,“取消”和“重试”。以下是我用来启动工作表的代码:

// failureSheet is a NSWindowController subclass

[NSApp beginSheet:[failureSheet window]
   modalForWindow:window
    modalDelegate:self
   didEndSelector:@selector(failureSheetDidEnd:returnCode:contextInfo:)
      contextInfo:nil];
问题在于,如果两个并发操作同时失败,则显示的当前工作表将被最后一条失败消息覆盖,然后用户的“重试”操作将仅重试最后一次失败的操作。理想情况下,我想“排队”这些故障表。如果两个操作同时失败,那么您应该看到两张工作表,一张接一张,允许用户分别取消或重试

我试过使用:

[NSApp runModalSessionForWindow:[failureSheet window]] 
这似乎是我想要的,但在我的情况下不起作用。也许它不是线程安全的

例如,下面的代码可以工作

- (void)displaySheet
{
    [NSApp beginSheet:[failureSheet window]
       modalForWindow:window
        modalDelegate:self
       didEndSelector:@selector(failureSheetDidEnd:returnCode:contextInfo:)
          contextInfo:nil];

    [NSApp runModalForWindow:[failureSheet window]];

    [NSApp endSheet:[failureSheet window]];

    [[failureSheet window] orderOut:nil];
}

// Calling this method from a button press works...
- (IBAction)testDisplayTwoSheets
{
    [self displaySheet];
    [self displaySheet];
}
但是,如果我有两个不同的线程操作(在主线程上)在完成时调用displaySheet,我只会看到一个工作表,当我关闭它时,模式会话仍在运行,我的应用程序基本上被卡住了


关于我做错了什么有什么建议吗?

如果你想让他们排队,那就让他们排队吧。创建结果对象的
NSMutableArray
(您可以使用操作、故障表本身,或提供工作表信息的数据对象;只要方便即可)。在
操作didfish:
(始终在主线程上运行,因此这里没有锁定问题)中,您可以执行以下操作:

[self.failures addObject:failure];
if ([[self window] attachedSheet] == nil)
{
    // Only start showing sheets if one isn't currently being shown.
    [self displayNextFailure];
}
那么你就有:

- (void)displayNextFailure
{
    if ([self.failures count] > 0)
    {
        MYFailure runFailure = [self.failures objectAtIndex:0];
        [self.failures removeObjectAtIndex:0];
        [displaySheetForFailure:failure];
    }
}
FailureSheetDiEnd:returnCode:contextInfo:
的末尾,只需确保调用
[self displayNextFailure]


也就是说,如果这种情况经常发生的话,这可能是一个糟糕的UI(没有什么比一张一张地显示更糟糕的了)。我可能会想办法修改现有工作表以显示多个错误。

我认为您没有正确使用“runModalForWindow:”命令。您不能同时使用[NSApp beginSheet…]和“runModalForWindow:”。一个是文档模式(应用程序保持运行,但带有工作表的窗口被锁定),另一个是应用程序模式(在工作表关闭之前停止整个应用程序中的所有内容)。您只需要应用程序模式对话框,因此请执行以下操作

对于应用程序模式,仅使用以下选项启动窗口:

[[failureSheet window] center];
[NSApp runModalForWindow:[failureSheet window]];
将“OK”按钮和其他按钮与常规iAction方法挂钩。当按下按钮时,他们会被呼叫。在iAction方法中,您需要执行以下操作来关闭窗口并处理操作:

-(IBAction)okBtnPressed:(id)sender {
    [NSApp stopModal];
    NSLog(@"ok button");
    [[sender window] close];
}

这肯定不会经常发生,这只是我正在努力处理的一个极端情况。我自己也在考虑实现类似的东西,不确定是否有更简单的方法。不过,这似乎很简单,谢谢你的回答。如果真的发生了这种情况,你应该改变你的用户界面,理智地处理它。您甚至可能希望将错误与作业队列混合在一起,以获得更像Xcode的构建结果窗口的内容。