繁体   English   中英

iPhone应用程序启动时间和Core Data迁移

[英]iPhone app launch times and Core Data migration

我有一个Core Data应用程序,我打算用新架构更新。 轻量级迁移似乎有效,但需要时间与数据库中的数据量成比例。 这发生在应用程序的didFinishLaunchingWithOptions阶段。

我想避免<app> failed to launch in time问题,因此我假设我无法在didFinishLaunchingWithOptions方法中保留迁移。

我假设最好的方法是在后台线程中执行迁移。 我还假设我需要推迟加载主ViewController,直到加载完成,以避免在初始化完成之前使用managedObjectContext

这是否有意义,是否有这种初始化的示例代码(可能在Apple示例项目中)?

您不能将迁移放在NSOperation因为它需要在主线程上运行。 您需要做的是退出-applicationDidFinishLaunching:方法而不触及Core Data堆栈。 如果您可以快速完成该方法(并且运行循环周期),那么您的应用程序将不会被终止,只要用户愿意完成您的迁移,您就可以使用该应用程序。

请参阅我的答案: 如何从Core Data自动轻量级迁移切换到手动?

2010年5月19日更新

澄清我对此的立场。 它本来就可能做任何事情。 但是,在后台线程上进行迁移是个坏主意 很难保证在迁移过程中堆栈永远不会被触及以及其他一些特定于线程的复杂问题。

可以做到,但它涉及高风险,这是完全没有必要的。 可以并且应该使用主线程来执行主Core Data堆栈的迁移。 很容易建立一个模态对话框让用户知道正在进行迁移,然后在主线程上执行迁移。

如果您的迁移过程需要花费大量时间,那么强烈建议您使用映射模型从自动迁移切换到手动迁移,以便您可以:

  • 如果需要,可以轻松退出迁移。
  • 如果用户退出您的应用程序,则以块的形式执行迁移。
  • 为用户提供有关迁移距离和完成时间的可靠反馈。

2015年12月15日更新

自从最初回答以来,已经发生了很多变化。

现在迁移答案是解雇他们在后台队列(通过dispatch_async中的NSPersistentStoreCoordinatoraddStore...调用)。

这也意味着您需要确保您的UI可以处理持久层是空的/在未知的时间段内不可用。 如何做到这一点取决于您的应用程序。

例如,您可以拥有一个临时UI,在持久层执行迁移时显示跳舞的猫。 用户体验取决于您。

但是,您希望让用户在迁移过程中创建数据。 这将使以后很难合并(如果有的话)。

对不起马库斯,我不得不恭敬地不以为然。 可以在后台迁移。

我的迁移在后台线程上运行。 在慢速设备上可能需要10秒以上,因此我在后台线程上启动它并使用特定的模态视图控制器来显示进度。

这样做的方法是将正常的加载顺序分为两个阶段。

阶段1)执行在启动时通常不需要托管对象的所有操作。 此阶段的结束由检查确定,以确定是否需要迁移。

阶段2)执行在启动时通常发生的所有需要​​托管对象的事情。 当不需要迁移时,此阶段是阶段1)的立即延续。

这样,无论迁移处理的持续时间如何,您的应用都会完成启动。

为了成功执行长迁移,我使用模态视图控制器向用户显示迁移进度的反馈。 然后我在后台线程中开始迁移,而模态视图控制器使用KVO更新它的进度条。

在迁移结束时,它关闭整个核心数据“堆栈”,并且对主线程的回调将关闭模态并继续到阶段2)。

这整个过程完美无瑕,尽管我仍然有一个开放的问题 ,即自动轻量级迁移的方式揭示了它的进展,就像手动迁移一样。

这是类似于ohhorob所描述的方法的概述。 不同之处在于我只是显示动画“升级......”HUD而不是更具信息性的进度条。

关键的一点是,在迁移有机会完成其任务之前,没有任何东西会尝试访问Core Data。 因此,我将可能触及Core Data的所有剩余启动代码(包括rootViewController的设置)移动到单独的方法中。 然后,我在迁移完成后调用此方法,如果不需要迁移,则立即调用此方法。 看起来像这样:

-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    [self.window makeKeyAndVisible];

    if ([MyManagedObject doesRequireMigration]) { // -doesRequireMigration is a method in my RHManagedObject Core Data framework
        [SVProgressHUD showWithStatus:NSLocalizedString(@"Upgrading...", nil) maskType:SVProgressHUDMaskTypeGradient];

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{

            // do your migration here

            dispatch_async(dispatch_get_main_queue(), ^{
                [SVProgressHUD showSuccessWithStatus:NSLocalizedString(@"Success!",nil)];
                [self postApplication:application didFinishLaunchingWithOptions:launchOptions];
            });
        });

    } else {
        [self postApplication:application didFinishLaunchingWithOptions:launchOptions];
    }

    return YES;
}

-(void)postApplication:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window.rootViewController = ....

    // anything else you want to run at launch
}

这种方法应该与监视启动时间的监视程序一起使用,因为迁移将在主线程之外进行。

我刚刚发现了一个非常简单的方法。 只需将所有调用包装在应用程序中:didFinishLaunching with dispatch_async到主线程。 这将立即返回,让您在主线程上执行升级。 但是,您应该设置窗口,以便在迁移时不显示空白屏幕。

- (void)application:didFinishLaunching {
     dispatch_async(main_queue) {
          // migration
     }
     self.window = ...
     return YES;
}

您可以将核心数据更新放入NSOperation ,可以通过覆盖操作的-main方法将其添加到didFinishLaunching...的操作队列中,并且可以在后台运行。

请查看本教程页面 ,了解所涉及的内容。 KVO与操作的isFinished属性一起使用以更新应用程序的状态 - 您可以使用此键的值来警告用户迁移仍在进行,例如,在显示任何数据之前。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM