Цель C - удалить / скрыть NSProgressIndicator после цикла

Я хотел бы обновить индикатор выполнения, используя поток, как изложено здесь. Я пытаюсь достичь этого результата:

  1. Индикатор выполнения становится видимым
  2. Индикатор выполнения обновляется с помощью цикла
  3. Индикатор выполнения исчезает

Это мой код:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{

        progressBar.hidden = NO;

        for (NSInteger i = 1; i <= progressBar.maxValue; i += 20){


                [NSThread sleepForTimeInterval:1.0];
                dispatch_async(dispatch_get_main_queue(), ^{
                    [progressBar setDoubleValue:(double)i];
                    [progressBar displayIfNeeded];
                });
            }

        progressBar.hidden = YES;

    });

Индикатор выполнения определяется в моем ViewController.h int следующим образом:

NSProgressIndicator *progressBar

Проблема в том, что полоса не удаляется в конце цикла, я не знаю, если progressBar.hidden = YES; работает таким образом.

Кто-нибудь может мне помочь? Фрагмент кода был бы действительно полезен, особенно если за ним следует объяснение.

2 ответа

Решение

То, что вы делаете, не хорошо по двум причинам.

Во-первых, спать нить не следует, если вы не владеете этой нитью или не знаете точно, какие обязанности у нее есть. Потоки для очередей принадлежат GCD, и вы должны держать свою работу на уровне очередей, а не на нижнем уровне. (Блоки из основной очереди всегда будут выполняться в главном потоке. В определенных, хотя и ограниченных, обстоятельствах блок из глобальной очереди может не выполняться в фоновом потоке.*)

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

Вы получили эту проблему наполовину правильно, отправив в основную очередь для setDoubleValue: звонок, но настройка hidden также должен быть в основном потоке.

Цикл for не является хорошим механизмом для обновления экрана. Я бы посоветовал переделать вашу процедуру, чтобы несколько раз вызывать метод. NSTimer построен для того, что ты делаешь. У вас не должно возникнуть проблем с поиском примера его использования.

Если вы хотите использовать GCD, я бы рекомендовал перейти на использование одного dispatch_after() вызов, многократно запускающий блок после задержки в основной очереди. Что-то вроде этого:

- (void)kickItOff
{
    self.progressBar.hidden = NO;
    [self updateProgress:0];
}

- (void)updateProgress:(double)progressValue
{
    if( self.progressBar.maxValue <= progressValue ){
        self.progressBar.hidden = YES;
        return;
    }

    dispatch_time_t oneSecond = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC));
    dispatch_after(oneSecond, dispatch_get_main_queue(), ^{

        [self.progressBar setDoubleValue:progressValue];
        [self updateProgress:progressValue + 20];
    });
}

Вы начинаете цикл обновления, вызывая kickItOff, затем updateProgress: устраивает зацикливание. Это позволяет основному потоку и циклу выполнения продолжать работать беспрепятственно, при этом гарантируя, что ваш код выполняется с нужными интервалами.


* Подробнее об этом: для того, чтобы пользовательский интерфейс действительно отображался на экране, основной цикл выполнения должен быть цикличным. Если основной поток спит, это не может произойти: весь пользовательский интерфейс заблокирован как для рисования, так и для приема ввода (и основная очередь отправки также не обрабатывается).

Ваш код неверен. Вы делаете вызовы пользовательского интерфейса из фонового потока, что недопустимо. Вы должны обернуть вызовы, которые изменяют состояние индикатора выполнения (включая установку скрытого значения True или False) в вызовах dispatch_async(dispatch_get_main_queue()){}

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
            dispatch_async(dispatch_get_main_queue())^{
              progressBar.hidden = NO;
            }

            for (NSInteger i = 1; i <= progressBar.maxValue; i += 20){


                    [NSThread sleepForTimeInterval:1.0];
                    dispatch_async(dispatch_get_main_queue(), ^{
                        [progressBar setDoubleValue:(double)i];
                        [progressBar displayIfNeeded];
                    });
                }

            dispatch_async(dispatch_get_main_queue())^{
              progressBar.hidden = YES;
            }

        });

Я так понимаю, это всего лишь учебное упражнение?

Другие вопросы по тегам