Grand Central Dispatch, viewWillAppear, viewDidAppear путаница в порядке выполнения

Я использую GCD для фоновой загрузки в моем приложении Tab Bar.

Первый шаг — выполнить фоновую загрузку в -viewWillAppear: (чтобы настроить некоторые основные данные перед загрузкой представления).

Второй шаг — остальная часть фоновой загрузки в -viewDidAppear:

По какой-то причине блок отправки в -viewDidAppear: вызывается перед блоком отправки в -viewWillAppear:.

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

Вот выдержки из моего кода (-viewWillAppear: и -viewDidAppear:):

-viewWillAppear:

- (void)viewWillAppear:(BOOL)animated {
    DLog(@"viewWillAppear method running");

    [super viewWillAppear:animated];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];

        [self setDiskCareerIds:[CareersParser idsFrom:@"disk"]];
        [self setDownloadedCareerIds:[CareersParser idsFrom:@"web"]];


        DLog(@"diskCareerIds after being set in viewWillAppear: %@", [self diskCareerIds])
        DLog(@"downloadedCareerIds after being set in viewWillAppear: %@", [self downloadedCareerIds])

        if ([[self downloadedCareerIds] isEqualToArray:[self diskCareerIds]]) {

            DLog(@"viewWillAppear: ids equal, loading careers from disk.");
            self.careers = [CareersParser loadCareersFromDisk];

            dispatch_async(dispatch_get_main_queue(), ^{

                [self.table reloadData];

                [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];


            });
        }

    });

    //[self downloadData];
}

-viewDidAppear:

- (void)viewDidAppear:(BOOL)animated {
    DLog(@"viewDidAppear method running");

    [super viewDidAppear:animated];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];

        if (![[self downloadedCareerIds] isEqualToArray:[self diskCareerIds]]) {

            DLog(@"ids not equal, saving careers to disk.");

            dispatch_async(dispatch_get_main_queue(), ^{

                [self showLoadingView];

            });

            [CareersParser saveCareersToDisk];
            self.careers = [CareersParser loadCareersFromDisk];
        }



        dispatch_async(dispatch_get_main_queue(), ^{

            [self.table reloadData];

            [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];

            [self removeLoadingView];

        });
    });

    //[self download3];

    //[self downloadData];
}

Просмотрите журнал отладки на странице Pastie.


person Peter Warbo    schedule 16.07.2011    source источник


Ответы (1)


Итак, вы печатаете это сообщение журнала в этом первом блоке (тот, который запланирован в viewWillAppear:) после того, как он выполнил кучу синтаксического анализа, а не когда он фактически начинает выполняться.

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

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

person Firoze Lafeer    schedule 16.07.2011
comment
Спасибо за ответ. Но в любом случае, разве методы не должны выполняться ПЕРВЫМИ в viewWillAppear? Я просто не понимаю, как viewDidAppear выполняется раньше? Однако я создал свою собственную очередь для отправки в viewWillAppear с помощью serialQueue = dispatch_queue_create("com.acando.viewwillappear", 0); и освобождаю ее dispatch_release(seialQueue); после запуска метода dispatch_async. Также делаем то же самое с методом viewDidAppear, но, конечно, меняя значения. Но диспетчерский вызов viewDidAppear по-прежнему выполняется ДО viewWillAppear. - person Peter Warbo; 16.07.2011
comment
Что ж, продолжайте и обновите свой вопрос текущим кодом теперь, когда вы используете последовательную очередь, и мы посмотрим, что происходит сейчас. Но в случае параллельной очереди, которая у вас была изначально, только потому, что блок A удаляется из очереди перед блоком B, это не означает, что он обязательно завершит выполнение первым. Если блоку A требуется много времени для разбора вещей, он может легко отстать от блока B, и поэтому вы получаете эти операторы журнала в блоке A после того, как блок B достиг своих операторов журнала. - person Firoze Lafeer; 16.07.2011
comment
Ага, кажется, теперь я понял. Вызовы отправки GCD не будут учитывать процесс выполнения viewWillAppear и viewDidAppear, когда они выполняются асинхронно. Я изменил свой диспетчерский вызов GCD в viewWillAppear:, чтобы он выполнялся синхронно, таким образом, он всегда завершается до завершения диспетчерского вызова GCD в viewDidAppear:. - person Peter Warbo; 16.07.2011
comment
Ну, это не совсем правильно. Очередь по-прежнему будет «первым пришел — первым вышел». Таким образом, первый блок запустится первым. НО, это не означает, что сначала он доберется до операторов журнала. Второй блок вполне может развиваться быстрее. Оба будут выполняться параллельно. Я думаю, что синхронизация — это излишество, и это может повлиять на вашу отзывчивость. - person Firoze Lafeer; 16.07.2011
comment
Это всего лишь две синхронные загрузки (не много данных, может 10-20кб). Во всяком случае, как бы я решил это асинхронно, потому что мне нужно загрузить эти данные, прежде чем другие данные смогут начать загрузку... - person Peter Warbo; 17.07.2011
comment
Что ж, невинные маленькие загрузки вдруг перестают быть такими безобидными, когда пользователь переключает сети или находится в зоне плохого сигнала или что-то в этом роде. Затем внезапно вы заморозили свой пользовательский интерфейс. Я думаю, вам следует взглянуть на последовательную очередь. Я знаю, вы сказали, что это не сработало, но если вы опубликуете, как именно вы это пытались, кто-то, вероятно, сможет помочь вам заставить это работать. - person Firoze Lafeer; 17.07.2011
comment
Извините за путаницу, я на самом деле заставил его работать, создав последовательную очередь в методе viewWillAppear:, а затем в viewDidAppear: я использую асинхронный вызов, и он работает, как и ожидалось. Методы в последовательной очереди завершаются до запуска методов в асинхронной очереди. - person Peter Warbo; 17.07.2011