Долгосрочная задача в приложении:didFinishLaunchesWithOptions:

У меня есть несколько длительных задач запуска (например, загрузка объектов Parse из локального хранилища данных) в моем приложении. Эти задачи должны быть завершены до того, как интерфейс начнет появляться. Приложение изначально было создано с использованием раскадровок, поэтому интерфейс начинает появляться автоматически после application:didFinishLaunchesWithOptions: Метод заканчивается. Я не могу заблокировать основной поток, потому что Parse SDK запускает все его обратные вызовы в главном потоке (поэтому блокировка приводит к взаимоблокировке). Мне также нужно отложить возврат из приложения:didFinishLaunchesWithOptions: завершить настройку. Итак, что я сделал, это:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Dispatch long-running tasks
    dispatch_group_t startup_group = dispatch_group_create();
    dispatch_group_async(startup_group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // Perform some long-running setup here
    });
    // Run main run loop until startup tasks finished (is it OK to do so?)
    while (dispatch_group_wait(startup_group, DISPATCH_TIME_NOW))
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
    return YES;
}

Это правильное использование NSRunLoop? Есть ли потенциальные предостережения? Можете ли вы предложить более элегантное (желательно GCD) решение?

ОБНОВИТЬ:

  1. Загрузка объекта Parse из локального хранилища данных (т. Е. Загрузка крошечного файла с SSD) не такая длительная операция, как загрузка чего-либо из Интернета. Задержка едва заметна, но достаточно велика, чтобы вызвать warnBlockingOperationOnMainThread, поэтому UX не проблема.
  2. Настоящая проблема - это (нечастые) сбои, вызванные вращением другого основного цикла выполнения над обычным основным циклом, что иногда приводит к повторному входу UIApplication методы делегата, которые, по-видимому, не являются потокобезопасными.
  3. Конечно, введение заставки является очевидным решением, но я искал что-то более простое и элегантное. У меня есть чувство, что это существует.

4 ответа

Хотя технически это работает в зависимости от того, что делает ваша установка (например, вызовы веб-службы), приложение может никогда не запуститься или запуститься с неожиданными результатами.

Более удобным для пользователя способом было бы добавить "загрузочный" контроллер вида на вашу раскадровку, который будет вашим видом посадки. Выполните вашу долгосрочную настройку здесь, предоставляя пользователю информацию / статус / все, что подходит, затем нажмите на свой оригинальный контроллер представления.

Отставание возвращения didFinishLaunchingWithOptions: Это действительно плохая идея. Это создаст плохое впечатление о вашем приложении и его плохой дизайн.

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

Вот ссылка на стороннюю библиотеку, которая облегчает отображение / скрытие наложения и установку некоторого текста в представлении.

Вы можете найти так много библиотек, как эта, просто в Google iOS HUD. HUD означает Heads Up Display, или вы можете создать собственную вещь, используя UIAnimation без труда.

Вообще говоря,

Я должен оценить тот код, который вы написали в том смысле, что вы остановили текущий поток или запустите цикл, используя While а также NSRunloop,

Может быть, вы можете немного изменить свой код и использовать его в каком-то другом месте вашей программы, чтобы остановить текущий поток или цикл выполнения и ждать чего-то.

NSDate *runloopUntill = [NSDate dateWithTimeIntervalSinceNow:0.1];
while (YourBoolFlag && [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode runloopUntill])

Следующая документация Apple даст вам глубокое понимание NSRunloop 1 2

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

 dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
        //Load your data here

        //Dispatch back to the main queue once your data is loaded.
        dispatch_async(dispatch_get_main_queue(), ^{
            //Update your UI here, or transition to the data centric view controller. The data loaded above is available at the time this block runs
        });
    });

Имхо, я думаю, что лучше делать свои вещи в главном View Controller и показывать пользователю что-то вроде пользовательского блесна: таким образом вы можете загружать свои данные, и пользователь будет знать, что происходит.

Никогда не бывает хорошей идеей так долго запускать приложение с пустым экраном или просто с экраном запуска без информации для пользователя.

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