NSOperation блокирует рисование интерфейса? - PullRequest
12 голосов
/ 21 января 2010

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

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

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

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

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

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

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

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

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

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

Ответы [ 2 ]

10 голосов
/ 21 января 2010

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

Таким образом, в этом методе вы можете принудительно запустить другой метод в основном потоке, используя 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;
}
0 голосов
/ 23 сентября 2015

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

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

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

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

...