Долгосрочная задача в приложении: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) решение?
ОБНОВИТЬ:
- Загрузка объекта Parse из локального хранилища данных (т. Е. Загрузка крошечного файла с SSD) не такая длительная операция, как загрузка чего-либо из Интернета. Задержка едва заметна, но достаточно велика, чтобы вызвать
warnBlockingOperationOnMainThread
, поэтому UX не проблема. - Настоящая проблема - это (нечастые) сбои, вызванные вращением другого основного цикла выполнения над обычным основным циклом, что иногда приводит к повторному входу
UIApplication
методы делегата, которые, по-видимому, не являются потокобезопасными. - Конечно, введение заставки является очевидным решением, но я искал что-то более простое и элегантное. У меня есть чувство, что это существует.
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 и показывать пользователю что-то вроде пользовательского блесна: таким образом вы можете загружать свои данные, и пользователь будет знать, что происходит.
Никогда не бывает хорошей идеей так долго запускать приложение с пустым экраном или просто с экраном запуска без информации для пользователя.