应用程序中长时间运行的任务:didFinishLaunchesWithOptions:

时间:2015-03-31 06:27:09

标签: ios multithreading asynchronous grand-central-dispatch nsrunloop

我的应用程序中有一些长时间运行的启动任务(比如从本地数据存储中加载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加载小文件)不是这么长操作,因为从Web加载某些东西。延迟几乎没有引起注意,但足以触发warnBlockingOperationOnMainThread,因此用户体验不是问题。
  2. 真正的问题是(偶发)崩溃是由于在常规主runloop之上旋转另一个主runloop引起的,这有时会导致重新进入UIApplication委托方法,这显然不是线程安全的。
  3. 原因介绍闪屏是一个明显的解决方案,但我正在寻找一种更简单和优雅的东西。我有感觉它存在。

4 个答案:

答案 0 :(得分:2)

虽然这在技术上取决于您的设置正在进行的操作(例如Web服务调用),但应用程序可能永远不会启动或启动并带有意外结果。

用户友好的方法是将“加载”视图控制器添加到您的故事板,这将是您的着陆视图。在此处执行长时间运行设置,同时向用户提供信息/状态/任何适当的设置,然后推送原始视图控制器。

答案 1 :(得分:2)

Imho 我认为最好在您的主视图控制器中执行您的操作并向用户显示内容,例如custom spinner以这种方式可以下载您的数据和用户知道会发生什么

从来没有一个好主意花费这么长时间来启动一个空白屏幕的应用程序或只是一个没有用户信息的启动屏幕。

答案 2 :(得分:2)

滞后didFinishLaunchingWithOptions:的回报真的是一个坏主意。这会给你的应用带来不好的印象,也会造成糟糕的设计。

相反,您可以登陆视图控制器并显示叠加层视图,其中显示进度活动你在完成的时候就可以解雇它。这是正确的方法。

以下是link到第三方库,可以轻松显示/隐藏叠加视图并在视图上设置一些文字。

你可以找到这么多像这样的图库只是google iOS HUD .HUD意味着Heads Up Display或你可以轻松地使用UIAnimation自行设计自定义的东西。

一般来说,

我必须理解您编写的代码,您已使用WhileNSRunloop停止当前流程或运行循环。

可能是你可以稍微改变你的代码并在程序的其他地方使用来停止当前的流程或runloop并等待某事。

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

以下Apple文档将为您提供NSRunloop 1 2

的深入见解

答案 3 :(得分:2)

这很有创意,但却是一个非常糟糕的主意。您应该将UI加载到故障安全状态 - 换句话说,假设网络不可用/慢,并且如果在呈现之前没有获取数据,则显示不会爆炸的视图。然后,您可以安全地在后台队列上加载数据,并在加载完成后更新视图或转换为以数据为中心的视图。

 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
        });
    });