Cocoa:处理线程结果,并对多个工作表进行排队

发布于 2024-08-22 07:21:21 字数 1511 浏览 10 评论 0原文

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

performSelectorOnMainThread:@selector(operationDidFinish:)
// and
performSelectorOnMainThread:@selector(operationDidFail:withMessage:)

当操作失败时,我启动一个显示错误消息的工作表,并向用户显示 2 个按钮:“取消”和“重试”。以下是我用来启动工作表的代码:

// failureSheet is a NSWindowController subclass

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

问题是,如果 2 个并发操作同时失败,则显示的当前工作表将被最后一条失败消息覆盖,然后用户的“重试”操作将被覆盖。仅重试上次失败的操作。理想情况下,我想对这些故障表进行“排队”。如果 2 个操作同时失败,那么您应该一张一张地看到 2 张纸,允许用户单独取消或重试它们。

我尝试过使用:

[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];
}

但是,如果我有 2 个不同的线程操作在完成时调用 displaySheet (在主线程上),我只看到一张表,当我关闭它时,模式会话仍在运行,我的应用程序正在运行基本上被卡住了。

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

I have a multithreaded application that has many concurrent operations going on at once. When each thread is finished it calls one of two methods on the main thread

performSelectorOnMainThread:@selector(operationDidFinish:)
// and
performSelectorOnMainThread:@selector(operationDidFail:withMessage:)

When an operation fails, I launch a sheet that displays the error message and present the user with 2 buttons, "cancel" and "try again". Here is the code I use to launch the sheet:

// failureSheet is a NSWindowController subclass

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

The problem is that if 2 concurrent operations fail at the same time, then the current sheet that is displayed gets overwritten with the last failure message, and then the user's "try again" action will only retry the last failed operation. Ideally, I would like to "queue" these failure sheets. If 2 operations fail at the same time then you should see 2 sheets one right after the other, allowing the user to cancel or retry them individually.

I've tried using:

[NSApp runModalSessionForWindow:[failureSheet window]] 

which seems to do what I want, but doesn't work in my situation. Maybe it isn't thread safe?

For example the following code works...

- (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];
}

However if I have 2 different threaded operations invoke displaySheet (on the main thread) when they are done, I only see one sheet and when I close it the modal session is still running and my app is essentially stuck.

Any suggestions as to what I'm doing wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

羁〃客ぐ 2024-08-29 07:21:21

如果您想对它们进行排队,那么只需对它们进行排队即可。创建结果对象的 NSMutableArray (您可以使用操作、失败表本身,或者为您提供表信息的数据对象;只要方便即可)。在 operationDidFinish: 中(始终在主线程上运行,因此这里没有锁定问题),您将执行如下操作:

[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];
    }
}

failureSheetDidEnd 的末尾: returnCode:contextInfo:,只需确保调用[self displayNextFailure]

也就是说,如果经常发生的话,这可能是一个可怕的用户界面(没有什么比一张又一张地显示更糟糕的了)。我可能会寻找修改现有工作表以显示多个错误的方法。

If you want to queue them, then just queue them. Create an NSMutableArray of result objects (you could use the operation, or the failure sheet itself, or a data object that gives you the information for the sheet; whatever is convenient). In operationDidFinish: (which always runs on the main thread, so no locking issues here), you'd do something like this:

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

Then you'd have:

- (void)displayNextFailure
{
    if ([self.failures count] > 0)
    {
        MYFailure runFailure = [self.failures objectAtIndex:0];
        [self.failures removeObjectAtIndex:0];
        [displaySheetForFailure:failure];
    }
}

And at the end of failureSheetDidEnd:returnCode:contextInfo:, just make sure to call [self displayNextFailure].

That said, this is probably a horrible UI if it can happen often (few things are worse than displaying sheet after sheet). I'd probably look for ways to modify the existing sheet to display multiple errors.

计㈡愣 2024-08-29 07:21:21

我认为您没有正确使用“runModalForWindow:”命令。您不会同时使用 [NSApp beginSheet...] 和“runModalForWindow:”。一种用于文档模式(应用程序保持运行,但包含工作表的窗口被锁定),另一种用于应用程序模式(停止整个应用程序中的所有内容,直到工作表被关闭)。您只需要应用程序模式对话框,所以这样做...

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

[[failureSheet window] center];
[NSApp runModalForWindow:[failureSheet window]];

将“确定”按钮和其他按钮与常规 IBAction 方法挂钩。当按下按钮时他们就会接到电话。在 IBAction 方法中,您需要执行类似的操作来关闭窗口并处理操作:

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

I don't think you're using the "runModalForWindow:" command properly. You wouldn't use both [NSApp beginSheet...] and "runModalForWindow:" at the same time. One is for document modal (where the application keeps running but the window with the sheet is locked) and one is for application modal (which stops everything in the entire app until the sheet is dismissed). You only want the application modal dialog so do this...

For application modal start the window with the following only:

[[failureSheet window] center];
[NSApp runModalForWindow:[failureSheet window]];

Hook the "OK" button and others to regular IBAction methods. They'll get called when a button is pressed. In the IBAction methods you need to do something like this to dismiss the window and process the action:

-(IBAction)okBtnPressed:(id)sender {
    [NSApp stopModal];
    NSLog(@"ok button");
    [[sender window] close];
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文