2011-12-30 3 views
4

Новичок ПредупреждениеКак запретить NSButton регистрировать прессы во время их отключения?

У меня есть простая, но неприятная проблема пытается отключить NSButton. Вот пример кода, чтобы проиллюстрировать проблему:

- (IBAction)taskTriggeredByNSButtonPress:(id)sender { 
[ibOutletToNSButton setEnabled:NO]; 
//A task is performed here that takes some time, during which time 
//the button should not respond to presses. 
//Once the task is completed, the button should become responsive again. 
[ibOutletToNSButton setEnabled:YES]; 
} 

Это то, что я наблюдаю. Я нажимаю кнопку. Кнопка становится отключенной (судя по ее выцветшему виду), и задача начинает выполняться. Пока кнопка отключена и выполнение задачи выполняется, я нажимаю кнопку второй раз. Ничего не происходит сразу, но как только задача завершена, метод taskTriggeredByNSButtonPress: вызывается во второй раз, предполагая, что нажатие второй кнопки было приостановлено, а затем активировано после повторного включения кнопки.

Я пробовал всевозможные хаки, чтобы предотвратить нажатие второй кнопки, включая введение временной задержки после инструкции [ibOutletToNSButton setEnabled:NO];, заставляя кнопку скрывать, а не отключать, закрывая кнопку с пользовательским представлением в течение времени он должен быть отключен, привязав статус кнопки enabled к собственности, и другие вещи, о которых я слишком смущен, чтобы упомянуть.

Пожалуйста, помогите мне понять, почему я не могу получить эту простую задачу отключения кнопки для работы.

ответ

4

Этот метод, как представляется, напрямую связан с кнопкой. Вы должны выполнить длительное действие в другом потоке, или основная runloop не будет доступна до тех пор, пока метод не вернется. Основная runloop не реагирует на события, пока она недоступна.

Во-первых, создать метод:

- (void)someLongTask: (id)sender { 
    // Do some long tasks… 

    // Now re-enable the button on the main thread (as required by Cocoa) 
    [sender performSelectorOnMainThread: @selector(setEnabled:) withObject: YES waitUntilDone: NO]; 
} 

Затем выполнить этот метод в отдельном потоке при нажатии кнопки он:

- (IBAction)buttonPushed: (id)sender { 
    [sender setEnabled: NO]; 
    [self performSelectorInBackground: @selector(someLongTask) withObject: nil]; 
} 

Вы можете заменить self в приведенном выше примере с объектом где -someLongTask проживает.

Благодаря многопоточности вы оставляете основную рабочую зону отдельно и стабильной. Возможно, ваша проблема будет решена. В противном случае вы решили проблему с отзывчивостью.

(Кстати, если метод вызывается только кнопкой, параметр sender установлен на кнопку. Таким образом, вам не нужно использовать выход в методе. Но это всего лишь намек.)

+2

Ваш метод 'buttonPushed:' немедленно включит кнопку, потому что для отсоединения потока требуется незначительное время. После завершения обработки вам необходимо обновить состояние кнопки, для чего потребуется запрос уведомления или метода из вторичного потока, используя 'performSelectorOnMainThread: withObject:'. –

+0

Вы, ребята, фантастичны. Я выполнил длинную задачу в фоновом потоке с помощью NSThread и, как предположил Роб, повторно включил кнопку из вторичного потока. Вуаля! Мало того, что пользовательский интерфейс остается отзывчивым, но кнопка отключает и повторно включает, как следует. И Рэнди, спасибо за подсказку об использовании 'sender'. Это хорошая эффективность. – scolfax

+0

@RobKeniger Извините, я не заметил свою ошибку. Спасибо, я обновлю его. –

3

Вы не должны выполнять задачи, требующие большого количества времени обработки в цикле основного события. Это то, что вы делаете, и весь пользовательский интерфейс приложения будет блокироваться во время выполнения вашего кода. Блокировка основной нити является причиной «Вращающейся пиццы смерти». Другими словами, не делайте этого.

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

Существует много способов сделать это.

Вы можете использовать методы NSThread, как предлагает Рэнди Марш. Однако вы должны быть очень осторожны, чтобы прочитать документацию, поскольку вы не можете просто вызвать какой-либо старый метод в фоновом потоке и ожидать, что он будет работать.Вы должны создать свой собственный пул авторесурсов в потоке и правильно распорядиться им. Вы не должны вызывать какие-либо методы, которые обновляют пользовательский интерфейс из вторичного потока. Вы должны быть предельно осторожны, что никакие переменные не будут доступны или изменены более чем по одному потоку за раз. Threading - это сложный бизнес.

Метод -performSelectorInBackground:withObject:NSObject по существу является простым способом использования NSThread и имеет те же оговорки.

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

Самый простой способ обработки это GCD (Grand Central Dispatch), что позволяет использовать встроенные блоки для записи фоновых процессов:

- (IBAction)taskTriggeredByNSButtonPress:(id)sender 
    { 
     [ibOutletToNSButton setEnabled:NO]; 

     //get a reference to the global GCD thread queue 
     dispatch_queue_t queue = dispatch_get_global_queue(0,0); 

     //get a reference to the main thread queue 
     dispatch_queue_t main = dispatch_get_main_queue(); 

     //perform long-running operation 
     dispatch_async(queue,^{ 
      NSLog(@"Doing something"); 
      sleep(15); 

      //update the UI on the main thread 
      dispatch_async(main,^{ 
       [ibOutletToNSButton setEnabled:YES]; 
      }); 
     }); 
    } 

НОД очень легкий и эффективный, и я настоятельно рекомендую вам использовать его, если это возможно.

В Apple Concurrency Programming Guide есть много информации и деталей, которые я рекомендую вам прочитать, хотя некоторые детали могут быть за пределами вас на этом этапе.

+2

Вы также можете использовать блоки с NSOperationQueue. –

+0

Благодарим вас за очень полезное описание вариантов выполнения тяжелой задачи в фоновом режиме и особенно для нюансов использования NSThread. Поскольку у меня был некоторый опыт работы с NSThread, я принял этот подход, и он отлично работал, как я упоминал выше. NSOperationQueue и GCD представляют собой изящные решения, которые я с нетерпением жду изучения и использования. Еще раз спасибо за эту большую помощь. – scolfax

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

  • Нет связанных вопросов^_^