Как мне обновить индикатор выполнения в Какао во время длинного цикла?
У меня есть while
цикл, который выполняется в течение многих секунд, и поэтому я хочу обновить индикатор выполнения (NSProgressIndicator) во время этого процесса, но он обновляется только один раз после завершения цикла. Кстати, то же самое происходит, если я хочу обновить текст метки.
Полагаю, мой цикл предотвращает появление других вещей в этом приложении. Должна быть другая техника. Это связано с темами или чем-то? Я на правильном пути? Может кто-нибудь дать мне простой пример, как "оптимизировать" мое приложение?
Мое приложение - Приложение Какао (Xcode 3.2.1) с этими двумя методами в моем Example_AppDelegate.m
:
// Этот метод запускается при нажатии кнопки запуска. - (IBAction)startIt:(id) отправитель { [progressbar setDoubleValue:0.0]; [прогрессбар startAnimation: отправитель]; бег = ДА; // это переменная экземпляра int i = 0; while (running) { if (i++ >= processAmount) { // processAmount - это что-то вроде 1000000 running = NO; Продолжить; } // Обновить индикатор выполнения double progr = (double)i / (double)processAmount; NSLog(@"progr: %f", progr); // Записывает значения от 0,0 до 1,0 [progressbar setDoubleValue:progr]; [progressbar needsDisplay]; // Мне это нужно? // Выполните здесь более тяжелую работу... } } // Этот метод запускается, когда нажимается кнопка остановки, но пока //startIt занят, нажатие кнопки остановки ничего не делает. - (IBAction)stopIt:(id)sender { NSLog(@"Stop it!"); работает = НЕТ; [прогрессбар stopAnimation: отправитель]; }
Я действительно новичок в Objective-C, какао и приложениях с пользовательским интерфейсом. Большое спасибо за любой полезный ответ.
4 ответа
Если вы строите для Snow Leopard, на мой взгляд, самое простое решение - использовать блоки и Grand Central Dispatch.
Следующий код показывает вам, как ваш startIt:
Метод будет выглядеть при использовании GCD.
Ваш stopIt:
Метод должен работать нормально, как вы написали. Причина, по которой он не работал раньше, заключается в том, что события мыши происходят в главном потоке, и, таким образом, кнопка не отвечает вам, потому что вы выполняете работу в главном потоке. Эта проблема должна быть решена сейчас, так как теперь работа над GCD перенесена в другой поток. Попробуйте код, и если он не сработает, дайте мне знать, и я посмотрю, не допустил ли я в нем ошибки.
// This method runs when a start button is clicked.
- (IBAction)startIt:(id)sender {
//Create the block that we wish to run on a different thread.
void (^progressBlock)(void);
progressBlock = ^{
[progressbar setDoubleValue:0.0];
[progressbar startAnimation:sender];
running = YES; // this is a instance variable
int i = 0;
while (running) {
if (i++ >= processAmount) { // processAmount is something like 1000000
running = NO;
continue;
}
// Update progress bar
double progr = (double)i / (double)processAmount;
NSLog(@"progr: %f", progr); // Logs values between 0.0 and 1.0
//NOTE: It is important to let all UI updates occur on the main thread,
//so we put the following UI updates on the main queue.
dispatch_async(dispatch_get_main_queue(), ^{
[progressbar setDoubleValue:progr];
[progressbar setNeedsDisplay:YES];
});
// Do some more hard work here...
}
}; //end of progressBlock
//Finally, run the block on a different thread.
dispatch_queue_t queue = dispatch_get_global_queue(0,0);
dispatch_async(queue,progressBlock);
}
Вы можете попробовать этот код..
[progressbar setUsesThreadedAnimation:YES];
Это сработало для меня, что является комбинацией ответов от других, которые, кажется, не работают (по крайней мере для меня) сами по себе:
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
//do something
dispatch_async(dispatch_get_main_queue(), ^{
progressBar.progress = (double)x / (double)[stockList count];
});
//do something else
});
Полагаю, мой цикл предотвращает появление других вещей в этом приложении.
Правильный. Вам нужно как-то это разбить.
Одним из способов может быть таймер, когда вы постепенно убираете очередь в обратном вызове таймера. Другим было бы заключить в оболочку код для обработки одного элемента в подклассе NSOperation, а также создать экземпляры этого класса (операций) и поместить их в NSOperationQueue.
Это связано с темами или чем-то?
Не обязательно. NSOperations выполняется на потоках, но NSOperationQueue будет обрабатывать порождение потока для вас. Таймер - это однопоточное решение: каждый таймер работает в потоке, на который вы его запланировали. Это может быть преимуществом или недостатком - вы решаете.
Смотрите раздел темы моего вступления к Какао для более подробной информации.