Как мне обновить индикатор выполнения в Какао во время длинного цикла? - PullRequest
5 голосов
/ 24 марта 2010

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

Полагаю, мой цикл предотвращает появление других вещей в этом приложении. Должна быть другая техника. Это связано с темами или чем-то? Я на правильном пути? Может кто-нибудь дать мне простой пример, как «оптимизировать» мое приложение?

Мое приложение - Приложение Какао (Xcode 3.2.1) с этими двумя методами в моем Example_AppDelegate.m:

// This method runs when a start button is clicked.
- (IBAction)startIt:(id)sender {
    [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
        [progressbar setDoubleValue:progr];
        [progressbar needsDisplay]; // Do I need this?

        // Do some more hard work here...
    }
}

// This method runs when a stop button is clicked, but as long
// as -startIt is busy, a click on the stop button does nothing.
- (IBAction)stopIt:(id)sender {
    NSLog(@"Stop it!");
    running = NO;
    [progressbar stopAnimation:sender];
}

Я действительно новичок в Objective-C, Какао и приложениях с пользовательским интерфейсом. Большое спасибо за любой полезный ответ.

Ответы [ 4 ]

15 голосов
/ 26 марта 2010

Если вы строите для 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);
}
3 голосов
/ 24 марта 2010

Вы можете попробовать этот код ..

[progressbar setUsesThreadedAnimation:YES];
2 голосов
/ 10 мая 2012

Это сработало для меня, что является комбинацией ответов от других, которые, кажется, не работают (по крайней мере, для меня) сами по себе:

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
});
2 голосов
/ 24 марта 2010

Полагаю, мой цикл предотвращает появление других вещей в этом приложении.

Правильно. Вам нужно как-то это разбить.

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

Это связано с темами или чем-то?

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

См. раздел потоков моего вступления к Какао для получения более подробной информации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...