【问题标题】:Core Data Light Weight Migration without being killed on launch核心数据轻量级迁移而不在启动时被杀死
【发布时间】:2018-04-01 03:04:36
【问题描述】:

我们的应用需要 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;

}

升级DatabaseViewController.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:

是否可以在使用轻量级迁移时做我想做的事情?

【问题讨论】:

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


    【解决方案1】:

    在加载持久存储时会发生迁移。使用NSPersistentContainer,只要您调用loadPersistentStores(),就会发生这种情况。在您的代码中,它看起来可能在 ZSSCoreDataManagerpersistentContainer 方法中。

    这与您使用什么方法 无关,而是您调用它的queue 问题。如果您长时间阻塞主队列,看门狗将杀死您的应用程序。当您这样做时,您的应用程序已停止响应。看门狗无法解释为什么会发生这种情况,它强制执行的规则是将其视为挂起的进程并将其杀死。这通常是一个好主意,因为只有当您的应用程序长时间无视用户输入时才会发生这种情况。如果您在 viewDidLoad 中处理迁移,则您在主队列中,这就是您的应用被终止的原因。

    我不确定仅使用NSPersistentContainer 是否可以解决此问题。该课程旨在处理最常见的情况,但您似乎不在此范围内。过去我所做的是使用NSPersistentStoreCoordinator 将持久存储加载到后台队列中,然后创建我需要的任何托管对象上下文。您可以先使用NSPersistentStoreCoordinator 处理迁移,然后在完成后加载NSPersistentContainer 来解决此问题。

    修复此问题部分取决于您是否可以重现崩溃。如果您现在不能这样做,那可能是第一步 - 到达您看到与用户相同的崩溃的地方,以便您知道何时修复了该崩溃。

    【讨论】:

    • 我更新了我的问题以显示我的NSPersistentContainer 是如何创建的。这就是我用来初始化Core Data 堆栈的全部内容,因为我们的应用程序是iOS 10。你是说我可以在后台队列上创建NSPersistentContainer 对象吗?
    • 我能够重现我的用户遇到的崩溃。一位用户向我提供了他们的 .sqlite 文件。由于我的设备已连接到调试器并且看门狗进程未运行,因此我从未见过崩溃。
    • @NicHubbard 我的回答已经涵盖了如何以及何时设置持久容器的详细信息。是不是有什么具体的不清楚的地方?
    • 我能够将我的 NSPersistentContainer 的创建移动到后台线程,它让应用程序加载,显示我的迁移 UI 并且一切都正确迁移。 NSPersistentContainer 线程安全吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-12
    • 2011-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多