Прерывание цикла с помощью NSNotification - PullRequest
2 голосов
/ 27 октября 2011

У меня есть класс, содержащий метод с циклом.Мне нужно иметь возможность разорвать цикл, если происходит определенное событие (например, нажатие кнопки).

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

Однако, если я нажимаю кнопку во время выполнения цикла, уведомление появляется после завершения цикла, а не прерывания цикла.

Я предполагаю, что это потому, что он работает в том же потоке.

Итак, как мне заставить NSNotificationCenter работать в фоновом / другом потоке?Это возможно?Или есть лучший способ сделать это?

Ответы [ 4 ]

4 голосов
/ 28 октября 2011

Это не просто центр уведомлений.

У меня есть класс, содержащий метод с циклом.Мне нужно иметь возможность разорвать цикл, если происходит определенное событие (например, нажатие кнопки).

События для этого нажатия кнопки поступают в основной поток.Если ваш цикл работает в основном потоке, то нажатие кнопки не будет обработано, пока ваш цикл не закончится.Уведомление публикуется немедленно относительно фактической обработки нажатия кнопки вашим приложением.

Или в виде списка:

  1. Пользователь нажимает кнопку.
  2. В вашем цикле не хватает дел и он возвращается.
  3. Нажатие кнопки поступает в ваше приложение и превращается кнопкой в ​​сообщение действия.
  4. Вы публикуете уведомление.
  5. Вы получите уведомление.

Задержка, которую вы видите, находится между шагами 1 и 2;шаг 4 происходит сразу после шага 3.

Уведомления в локальном (не распределенном) NSNotificationCenter отправляются в потоке, из которого вы их публикуете, поэтому публикация его из вашего метода действия означает, что оно будет отправлено в основном потоке,Это нормально и нормально.

Переместите цикл , а не уведомление, в фоновый поток, очередь отправки или очередь операций.Если вы используете очередь операций, вам может вообще не понадобиться уведомление, так как вы можете указать очереди операций отменить все ожидающие операции.(Ваши операции должны будут в любое подходящее время проверять, были ли они отменены; по причинам, обсуждавшимся ранее , уничтожение потока / операции в произвольное время - плохая идея.)

Фоновые потоки, блоки и операции могут при необходимости возвращаться к основному потоку (например, для обновления пользовательского интерфейса).Чтобы отправить сообщение через цикл выполнения основного потока, используйте performSelectorOnMainThread:withObject:waitUntilDone:.Чтобы отправить блок в основной поток, используйте dispatch_async и dispatch_get_main_queue.Чтобы запланировать операцию в главном потоке, добавьте ее в [NSOperationQueue mainQueue].

. Для получения дополнительной информации прочтите Руководство по программированию параллелизма и Темы программирования уведомлений .

3 голосов
/ 28 октября 2011

Я бы запустил цикл в отдельном потоке. Еще лучше, сделайте это NSOperation, чтобы вы могли позвонить [.. cancel]. Просто убедитесь, что вы используете performSelectorOnMainThread при обновлении пользовательского интерфейса из объекта NSOperation. Не стоит иметь длительный цикл в главном потоке.

3 голосов
/ 27 октября 2011

Я бы запустил ваш цикл в отдельном потоке и имел бы переменную экземпляра BOOL abort;. Когда приходит уведомление о нажатии кнопки, задайте abort = TRUE;, затем в цикле проверьте это значение и выйдите, если оно истинно.

2 голосов
/ 27 октября 2011

Вы не можете поместить центр уведомлений в другой поток.Этот объект находится вне вашего контроля.Проблема не столько в том, что они находятся в том же потоке, сколько в том, что вы не позволяете run loop , который отвечает за обработку нажатия кнопки, что-либо делать.(Существует один и только один цикл выполнения на поток.) ​​Как было сказано и edsko, и Питером Хоси, само нажатие кнопки и фактически весь ваш пользовательский интерфейс останавливаются во время работы цикла.Как правило, рекомендуется поместить длительные операции в фоновый поток, а затем перезвонить основному потоку для обновления пользовательского интерфейса, performSelectorOnMainThread:withObject:waitUntilDone: - простой способ сделать такой обратный вызов.

Тем не менее, если вы хотите сохранить цикл в главном потоке, вам необходимо периодически разрешать элементу управления вернуться в цикл выполнения, чтобы нажатие кнопки было зарегистрировано.Есть два способа сделать это.Во-первых, вы можете явно кратко дать элемент управления цикла выполнения на каждой итерации цикла:

while( !buttonWasPressed ){

    // Do work...

    // Let the run loop do some processing before the next iteration.
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
}

Или вы можете создать один метод, который состоит только из кода из вашего цикла, и использовать performSelector:withObject:afterDelay: для многократного вызова метода, в то же время позволяя циклу выполнения работать:

- (void) loopTheLoop {

    if( buttonWasPressed ) return;

    // Do work...

    // Run this method again as soon as possible.
    [self performSelector:@selector(loopTheLoop)
               withObject:nil
               afterDelay:0.0];
}
...