核心数据轻量级迁移,不会在发布时被杀死

时间:2017-10-20 00:19:02

标签: ios objective-c core-data core-data-migration

我们的应用需要Core Data轻量级迁移,因为我们已向实体添加了一些属性。

在TestFlight上向我们的Beta版测试人员发布更新后,我们收到其中一些人的报告称应用程序在启动时崩溃了。在获得崩溃日志之后,我们意识到跳板监视器正在杀死应用程序,因为迁移时间过长。

在线搜索资源之后,似乎可以通过首先检查是否需要迁移,不接触核心数据堆栈以及选择执行迁移来卸载application:didFinishLaunchingWithOptions:之外的迁移。另一个视图控制器这是我想要做的:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    if ([[ZSSCoreDataManager sharedService] migrationRequired]) {

        UpgradeDatabaseViewController *upgrade = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:@"UpgradeDatabaseViewController"];
        self.window.rootViewController = upgrade;

    }

    return YES;

}

迁移测试:

- (BOOL)migrationRequired {

    NSManagedObjectModel *mom = [NSManagedObjectModel mergedModelFromBundles:nil];
    NSPersistentStoreCoordinator *persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];

    NSError *error = nil;
    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreDataMergeTest.sqlite"];

    // Determine if a migration is needed
    NSDictionary *sourceMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType URL:storeURL options:nil error:&error];
    NSManagedObjectModel *destinationModel = [persistentStoreCoordinator managedObjectModel];
    BOOL pscCompatibile = [destinationModel isConfiguration:nil compatibleWithStoreMetadata:sourceMetadata];

    return !pscCompatibile;

}

UpgradeDatabaseViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];

    NSLog(@"Starting migration");

    // Start migration by accessing th epersistent container
    [[ZSSCoreDataManager sharedService] persistentContainer];

    NSLog(@"Ended migration");

    UIWindow *window = [UIApplication sharedApplication].keyWindow;
    UINavigationController *nav = [self.storyboard instantiateViewControllerWithIdentifier:@"MainNav"];
    window.rootViewController = nav;

}

持久性容器:

- (NSPersistentContainer *)persistentContainer {

    @synchronized (self) {

        if (_persistentContainer != nil) {
            return _persistentContainer;
        }

        _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"Model"];

        // Store description
        NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreDataMergeTest.sqlite"];
        NSPersistentStoreDescription *description = [NSPersistentStoreDescription persistentStoreDescriptionWithURL:storeURL];

        description.shouldInferMappingModelAutomatically = YES;
        description.shouldMigrateStoreAutomatically = YES;

        description.type = NSSQLiteStoreType;
        _persistentContainer.persistentStoreDescriptions = @[description];

        [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error){
            if (error != nil) {
                NSLog(@"Unresolved error %@, %@", error, error.userInfo);
                //abort();
            }
        }];

        _persistentContainer.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
        _persistentContainer.viewContext.undoManager = nil; // We don't need undo so set it to nil.
        _persistentContainer.viewContext.shouldDeleteInaccessibleFaults = YES;
        _persistentContainer.viewContext.automaticallyMergesChangesFromParent = YES;

    }

    return _persistentContainer;

}

可悲的是,这仍然无法解决问题,因为跳板仍然会杀死应用程序(当设备未插入且调试器正在运行时)。

这里有什么东西我做错了吗?无论我们是否不启动数据库,轻量级迁移是否都发生在application:didFinishLaunchingWithOptions:中?

在使用轻量级迁移时,甚至可以做我想做的事情吗?

1 个答案:

答案 0 :(得分:2)

加载持久性存储时会发生迁移。使用NSPersistentContainer时,只要您拨打loadPersistentStores(),就会发生这种情况。在您的代码中,它看起来可能是ZSSCoreDataManager的{​​{1}}方法。

这不是关于您执行此操作的方法,而是您调用它的队列的问题。如果您长时间阻止主队列,看门狗将终止您的应用程序。当您这样做时,您的应用程序已停止响应。看门狗无法判断为什么会发生这种情况,它强制执行的规则是将其视为挂起进程并将其终止。这通常是一个好主意,因为它只发生在你的应用程序长时间忽略用户输入的情况下。如果你在persistentContainer处理迁移,那么你就在主队列中,这就是你的应用被杀的原因。

我不确定这是否只能通过viewDidLoad修复。该课程旨在处理最常见的情况,但你似乎在那之外。在过去,我所做的是使用NSPersistentContainer在后​​台队列上加载持久性存储,然后创建我需要的任何托管对象上下文。您可以通过首先使用NSPersistentStoreCoordinator来处理迁移,然后在完成时加载NSPersistentStoreCoordinator来处理此问题。

修复此问题部分取决于您是否可以重现崩溃。如果您现在不能这样做,那可能是第一步 - 到达您看到与用户相同的崩溃的位置,以便您可以判断崩溃何时得到解决。