Подтвердить что ты не робот

NSOperation блокирует рисование UI?

Я после некоторого совета по использованию NSOperation и рисунка:

У меня есть основной поток, создающий мой подкласс NSOperation, который затем добавляет его к NSOperationQueue.

My NSOperation выполняет некоторую тяжелую обработку, он должен зацикливаться в методе main() в течение нескольких минут, постоянно обрабатывая некоторую работу, но на данный момент у меня просто есть цикл while() со сном (1) внутри, который устанавливается всего 5 раз (для тестирования).

Основной (оригинальный) поток, который порождает этот NSOperation, отвечает за рисование в представлении и обновление пользовательского интерфейса.

Я намеревался, чтобы поток NSOperation использовал уведомление, чтобы сообщить основному потоку, что он сделал некоторую обработку, на данный момент это уведомление отправляется один раз каждый раз, когда он проходит через цикл while() (то есть: один раз второй, потому что он просто делает сон (1)). Основной поток (вид) регистрируется для получения этих уведомлений.

Уведомления сразу попадают в основной поток и кажутся асинхронно выглядящими просто отлично. Похоже, что оба потока работают как ожидалось... то есть - одновременно. (Я использую NSLog(), чтобы просто проверить, когда каждый поток отправляет и получает уведомление).

Когда представление получает уведомление и вызывается его метод обработчика, я просто увеличиваю целочисленную переменную и пытаюсь сделать это для представления (как строку, конечно). При тестировании код в drawRect: очень просто рисует это целое число (как строку) на экране.

Однако: здесь моя проблема (извините, что понадобилось немного времени, чтобы добраться сюда): когда основной поток (представление) получает уведомление от NSOperation, он обновляет это тестовое целое и вызывает [self setNeedsDisplay]. Однако представление не перерисовывается до завершения NSOperation! Я ожидал, что NSOperation, будучи отдельным потоком, не будет иметь возможности блокировать цикл событий основного потока, но кажется, что это то, что происходит. Когда NSOperation заканчивается и возвращается его main(), вид, наконец, перерисовывается сразу.

Возможно, я неправильно использую NSOperation. Я использую его в режиме "несовместимый", но, несмотря на мое имя, я понимаю, что он все еще создает новый поток и позволяет асинхронную обработку.

Любая помощь или совет очень ценится, если вы хотите увидеть какой-то код, просто дайте мне знать.

4b9b3361

Ответ 1

Метод в наблюдателе, который выполняется в ответ на ваше уведомление, не выполняется в основном потоке.

Итак, в этом методе вы можете принудительно запустить другой метод в основном потоке с помощью performSelectorOnMainThread:withObject:waitUntilDone:.

Например:

MyOperation.m

- (void)main {
    for (int i = 1; i <= 5; i++) {
        sleep(1);
        [[NSNotificationCenter defaultCenter] postNotificationName:@"GTCNotification" object:[NSNumber numberWithInteger:i]];
    }
}

MyViewController.m

- (void)setupOperation {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(myNotificationResponse:) name:@"GTCNotification" object:nil];

    NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
    MyOperation *myOp = [[MyOperation alloc] init];

    [opQueue addOperation:myOp];

    [myOp release];
    [opQueue release];
}

- (void)myNotificationResponse:(NSNotification*)note {
    NSNumber *count = [note object];
    [self performSelectorOnMainThread:@selector(updateView:) withObject:count waitUntilDone:YES];
}

- (void)updateView:(NSNumber*)count {
    countLabel.text = count.stringValue;
}

Ответ 2

Большинство людей знают, что любая задача, связанная с отображением, должна выполняться в основном потоке. Однако, как непосредственное следствие этого, любое изменение свойства привязки Cocoa, которое может повлиять на чертеж, также должно быть изменено в основном потоке (поскольку триггеры KVO будут обрабатываться в потоке, из которого они запускаются). Это является неожиданностью для большинства людей: в частности, небезопасно вызывать [self setNeedsDisplay] из потока, отличного от основного потока.

Как упоминалось gerry, ваша NSNotification не обрабатывается в основном потоке, она обрабатывается в потоке, из которого он отправлен. Следовательно, в вашем обработчике NSNotification вы должны отправить свои команды обратно в основной поток, если они связаны с отображением, или если они влияют на привязки Cocoa. @performSelector работает, но с очередями я нашел более легкий и понятный метод:

[ [ NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
    /* Your main-thread code here */ }];

Он избегает определения вторичной вспомогательной функции, единственная цель которой вызывается из основного потока. mainQueue было определено только в > 10.6.