2011-10-27 10 views
2

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

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

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

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

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

ответ

4

Это не только центр уведомлений.

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

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

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

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

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

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

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

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

Для получения дополнительной информации прочитайте Concurrency Programming Guide и Notification Programming Topics.

3

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

+0

Петля обновляет пользовательский интерфейс, поэтому он должен оставаться в основном потоке – John

2

Вы не можете поместить центр уведомлений в другой поток. Этот объект находится вне вашего контроля. Проблема заключается не в том, что они находятся в том же потоке, что и вы не позволяете run loop, который отвечает за обработку нажатия кнопки, чтобы что-либо сделать. (В каждом потоке есть один и только один цикл цикла.) Как было указано как edsko, так и Peter Hosey, кнопка сама нажата, и фактически весь ваш пользовательский интерфейс остановлен во время работы вашего цикла. Как правило, неплохо прикладывать длительные операции к фоновому потоку, а затем переходить к основному потоку для обновления пользовательского интерфейса, 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]; 
} 
3

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

Смежные вопросы