UIAlertview зависает, пока поток в фоновом режиме загружает данные

У меня есть UIBarButtonItem под названием «Обновить данные». Когда пользователь нажимает на нее, приложение должно обновить свои данные. При нажатии этой кнопки запускаются веб-службы и переносятся данные xml, и они имеют порядок 30000-40000 записей. Поэтому, чтобы пользовательский интерфейс не зависал, я написал фоновый поток и загрузил его туда.

- (void)refreshDataAction
{
NSLog(@"Refresh Data");
//Put up an alert box indicating user to wait while data is loading.

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Data is Loading"
                                                        message:@"Please wait while data is being refreshed."
                                                       delegate:self
                                              cancelButtonTitle:@"OK"
                                          otherButtonTitles:nil, nil];
alert.tag = 10;
[alert show];
 }

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSLog(@"hit in clickedbuttonatindex alertview at 259");
self.refreshActIndicator.hidden = NO;
[self.refreshActIndicator startAnimating];
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
                                         (unsigned long)NULL), ^(void) {
    [self getAllCustomerValues];

    NSError *nwerror = nil;
    if (![self.secondMOC save:&nwerror])
    {
        NSLog(@"209 Failed to save second MOC");
    }
    else
    {
        //NSLog(@"saved success");
    }
 });
}

Как вы можете видеть, появляется представление предупреждений. Я нажимаю ОК, и коробка все еще "почернела" и висит на экране. А через 7-8 секунд окошко исчезает, и начинается анимация индикатора активности. Моя цель - получить что-то подобное. 1. Пользователь нажимает кнопку обновления данных. 2.Появится вид предупреждений. 3. Пользователь нажимает ОК. 4. Поле предупреждения исчезает, и сразу же начинает работать фоновый поток, и я вижу, что индикатор активности работает / анимируется.

Пользователь будет знать, что нужно подождать, пока индикатор активности будет анимирован. Итак, как мне заставить приложение начинать анимацию сразу после того, как пользователь нажимает «ОК», и предупреждение ОК не будет затемнено. Понятно? Если вам нужна дополнительная информация, спросите. Спасибо

РЕДАКТИРОВАТЬ: на этом экране работает фоновый поток, когда он загружается в первый раз за этот день. Перед этим есть экран. На нем у меня есть кнопка продолжения, и нажатие на нее запускает фоновый поток, который делает то же самое, что и здесь. Я не убиваю его или что-то в этом роде.

РЕДАКТИРОВАТЬ 2:

 - (void)refreshDataAction
   {
NSLog(@"Refresh Data");
self.txtCustomerSearch.text =@"";
[self cleanUPPreviousLabels];

//Put up an alert box indicating user to wait while data is loading.

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Data is Loading"
                                                message:@"Please wait while data is being refreshed."
                                               delegate:self
                                      cancelButtonTitle:@"OK"
                                      otherButtonTitles:nil, nil];
//alert.tag = 10;
//[alert show];
[alert performSelectorOnMainThread:@selector(show) withObject:nil waitUntilDone:FALSE];

NSLog(@"hit in willpresenet alertview at 221");
    self.refreshActIndicator.hidden = NO;
    [self.refreshActIndicator startAnimating];
NSLog(@"Dispatching");

//Disable the view and all the other controls
self.txtCustomerSearch.userInteractionEnabled =NO;
self.txtCustomerSearch.enabled =NO;
self.btnSearch.enabled =NO;
self.btnSearch.userInteractionEnabled = NO;
self.scrollView.userInteractionEnabled = NO;
//self.view.userInteractionEnabled =NO;
self.chkButton.enabled = NO;


[self deletePreviousValues]; 

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
    NSLog(@"Getting customer values");
    [self getAllCustomerValues];
    NSLog(@"Got customer values");

    NSError *nwerror = nil;
    if (![self.secondMOC save:&nwerror])
    {
        NSLog(@"209 Failed to save second MOC");
    }
    else
    {
        //NSLog(@"saved success");
    }


    self.txtCustomerSearch.userInteractionEnabled = YES;
    self.txtCustomerSearch.enabled =YES;
    self.btnSearch.enabled =YES;
    self.btnSearch.userInteractionEnabled = YES;
    self.scrollView.userInteractionEnabled = YES;
    self.view.userInteractionEnabled =YES;
    self.chkButton.enabled = YES;

    [self.refreshActIndicator stopAnimating];

    NSLog(@"Saved");


});
NSLog(@"Dispatched");

}


person RookieAppler    schedule 21.02.2013    source источник
comment
Метод getAllCustomerValues ​​синхронный или нет?   -  person gsach    schedule 21.02.2013
comment
@GeorgeSachin. Как мне это узнать?   -  person RookieAppler    schedule 21.02.2013
comment
@GeorgeSachin - поскольку он выполняется в асинхронной очереди, он будет выполняться асинхронно.   -  person rmaddy    schedule 21.02.2013


Ответы (5)


Попробуйте изменить

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
                                     (unsigned long)NULL), ^(void) {

к фоновому приоритету

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,
                                     (unsigned long)NULL), ^(void) {
person Ric    schedule 21.02.2013
comment
@ Ric.No. Поменял, но он все еще такой. Окно предупреждения потемнело и через 7-8 секунд оно пропадает, а затем запускается моя анимация индикатора активности. Также я отредактировал некоторую информацию в своем исходном посте. Пожалуйста, проверьте это. - person RookieAppler; 21.02.2013

Используйте alertView:didDismissWithButtonIndex: вместо alertView:clickedButtonAtIndex:. Таким образом, предупреждение уже закрывается при вызове вашего кода.

Вопрос - зачем заморачиваться с представлением предупреждений? Пользователь знает, что нажал на значок «обновить». Вы уже показываете индикатор активности, и предупреждение не дает пользователю возможности отменить обновление.

Обновление: вызов dispatch_async кажется немного неработающим. Попробуй это:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self getAllCustomerValues];

    NSError *nwerror = nil;
    if (![self.secondMOC save:&nwerror]) {
        NSLog(@"209 Failed to save second MOC");
    } else {
        //NSLog(@"saved success");
    }
});
person rmaddy    schedule 21.02.2013
comment
Что ж, когда у меня не было просмотра предупреждений, то при нажатии кнопки обновления все еще требовалось 7-8 секунд, чтобы отобразить индикатор активности. Так что экран замер на 7-8 секунд, ничего не происходило. Нет индикатора. Ничего. Я не знаю, почему это занимает такое время, но теперь я ввел представление предупреждений, чтобы по крайней мере сообщить им, что что-то происходит. - person RookieAppler; 21.02.2013
comment
То, что вы описываете, указывает на то, что загрузка данных (getAllCustomerValues) действительно выполняется в основном потоке. Но этого не должно быть, поскольку вы запускаете этот код в фоновой очереди. Смотрите мой обновленный ответ. - person rmaddy; 21.02.2013
comment
Я изменился, как ты предлагал. dismissWithButtonIndex и измените dispatch_async. Но это только так. Нажимаю обновить данные. Откроется окно предупреждений. Нажмите ОК, чтобы закрыть. Поле предупреждения исчезло. Задержка 7-8 секунд (ничего не происходит), затем индикатор начинает вращаться / анимироваться. - person RookieAppler; 21.02.2013
comment
Это не имеет никакого смысла. См. Ответ Рика для добавления некоторого ведения журнала. Вы должны увидеть журнал «Отправлено» перед любым журналом внутри вызова dispatch_async. - person rmaddy; 22.02.2013

Может помочь добавить несколько NSLogs, чтобы увидеть, где он висит, и остановить индикатор активности, чтобы увидеть, происходит ли полное зависание из-за отправки.

 NSLog(@"hit in clickedbuttonatindex alertview at 259");
 self.refreshActIndicator.hidden = NO;
 [self.refreshActIndicator startAnimating];
 NSLog(@"Dispatching");
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
                                     (unsigned long)NULL), ^(void) {
    NSLog(@"Getting customer values");
    [self getAllCustomerValues];
    NSLog(@"Got customer values");

    NSError *nwerror = nil;
    if (![self.secondMOC save:&nwerror])
    {
        NSLog(@"209 Failed to save second MOC");
    }
    else
    {
        //NSLog(@"saved success");
    }
    [self.refreshActIndicator stopAnimating];
    NSLog(@"Saved");
 });
 NSLog(@"Dispatched");

Что происходит, так это то, что основная очередь (UI) по какой-то причине останавливается, что приводит к зависанию. Поскольку он остановлен, такие вещи, как индикаторы активности обновления, не будут анимироваться, даже если вы сказали им начать анимацию. И помните, что вы не можете обновить пользовательский интерфейс в асинхронном режиме, поэтому не следует использовать secondMOC, чтобы заставить какие-либо таблицы перезагружать данные и [self getAllCustomerValues]; не должен делать никаких действий с пользовательским интерфейсом.

person Ric    schedule 21.02.2013
comment
Таким образом, в симуляторе вывод будет следующим: нажмите кнопку clickedbuttonatindex alertview по адресу 259. Диспетчеризация. Получение значений клиента. Отправлено. Получение значений клиента. Сохранено. Также на симуляторе вообще нет задержки (без ожидания 7-8 секунд). На устройство нужно время. - person RookieAppler; 22.02.2013
comment
Когда вы отлаживаете на устройстве - запускаете на устройстве iOS - как выглядит вывод? - person Ric; 22.02.2013
comment
Когда я подключаю свое устройство к Mac, в xcode я получаю iPad Phani, iPad 6.1 Simulator, iPhone 6.1 Simulator. Я выбираю ipad phani и запускаю его, а затем не вижу вывода в моем Xcode. Что ты сказал? Не могли бы вы немного объяснить. Спасибо - person RookieAppler; 22.02.2013
comment
Извините, попробуйте «Отладка на устройстве iOS». Должен появиться результат. - person Ric; 22.02.2013
comment
Я использую ios6.1. Я не вижу опцию «Отладка» на устройстве iOS. Где это? - person RookieAppler; 22.02.2013
comment
Подождите, он должен показать результат. Как это: stackoverflow .com / questions / 3377309 / - person Ric; 22.02.2013
comment
Я пытался выполнить эти шаги, но они не привели меня к правильному выводу. Сначала, когда я подключаю свое устройство и запускаю, оно говорит: «Не удалось запустить» ‹appname› не удалось получить задачу для процесса 253. Затем я изменил подпись кода на разработчика iPhone и запустил, но он говорит о другой ошибке. - person RookieAppler; 22.02.2013
comment
Я думаю, вам нужно разобраться, почему это происходит, прежде чем пытаться исправить зависание. - person Ric; 22.02.2013
comment
Причина здесь - [self deletePreviousValues]; Проходит 13-секундная задержка. Как я могу это предотвратить? - person RookieAppler; 22.02.2013
comment
Я не уверен, что такое deletePreviousValues. Я могу только догадываться, что это должна быть первая строка в файле dispatch_async. - person Ric; 22.02.2013

Мы используем MBProgressHUD (https://github.com/jdg/MBProgressHUD), который делает такое уведомление пользовательского интерфейса легкий. Обычно вы предоставляете вызываемый метод, который может проверять атомарное значение BOOL. Установите BOOL при установке HUD, когда фоновый процесс завершится, очистите BOOL, и HUD автоматически исчезнет.

person ahwulf    schedule 21.02.2013

Вы говорите, что все это происходит в фоновом потоке - убедитесь, что вы загружаете UIAlertView в основной поток:

//[alert show];
[alert performSelectorOnMainThread:@selector(show) withObject:nil waitUntilDone:FALSE];

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

person escrafford    schedule 21.02.2013
comment
Я отредактировал свой код, как вы сказали. Это не сработало. Я разместил здесь исправленный метод refreshDataAction. Взгляни и скажи мне, если найдешь что-нибудь не так. Спасибо - person RookieAppler; 22.02.2013