Какао: обработка результатов потока и постановка в очередь нескольких листов - PullRequest
1 голос
/ 22 февраля 2010

У меня есть многопоточное приложение, в котором одновременно выполняется много одновременных операций. Когда каждый поток завершается, он вызывает один из двух методов основного потока

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

Проблема заключается в том, что если одновременно происходит сбой 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 (в главном потоке), когда они завершены, я вижу только один лист, и когда я закрываю его, модальный сеанс все еще выполняется, и мое приложение по существу застряло.

Какие-нибудь предложения относительно того, что я делаю неправильно?

Ответы [ 2 ]

3 голосов
/ 22 февраля 2010

Если вы хотите поставить их в очередь, просто поставьте их в очередь. Создайте 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].

Тем не менее, это, вероятно, ужасный интерфейс, если он может происходить часто (немногие вещи хуже, чем отображение листа за листом). Вероятно, я бы искал способы изменить существующий лист для отображения нескольких ошибок.

0 голосов
/ 24 февраля 2010

Не думаю, что вы правильно используете команду 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];
}
...