ios - 核心数据轻量级迁移而不会在启动时被杀死

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

我们的应用程序需要一个核心数据轻量级迁移,因为我们已经为我们的实体添加了一些属性。

在 TestFlight 上向我们的 beta 测试人员发布更新后,我们从其中一些人那里收到了应用程序在启动时崩溃的报告。在获取崩溃日志后,我们意识到 springboard watchdog 正在终止应用程序,因为迁移时间太长。

在线搜索资源后,似乎可以通过首先检查是否需要迁移而不触及 Core Data 堆栈来卸载 application:didFinishLaunchingWithOptions: 之外的迁移,并且选择在另一个 View Controller 中进行迁移。这是我正在尝试做的事情:

- (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;

}

升级数据库 View Controller .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: 中发生,而不管我们是否不初始化数据库?

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

最佳答案

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

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

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

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

关于ios - 核心数据轻量级迁移而不会在启动时被杀死,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46840680/

相关文章:

ios - 核心数据替代多对多集

ios - 使用 Kiwi 检查模拟对象的方法参数

启用绘图的 iOS 四舍五入 UIView 角

iphone - 何时使用 Core Data 的 NSMainQueueConcurrencyType?

iphone - 从 Xcode 更改按钮文本?

ios - Swift CoreData sortdescriptions 不起作用

iOS - fatal error : unexpectedly found nil while unwrapping an Optional value when reloadData() is called

ios - UISearchBar:更改输入字段的背景颜色

objective-c - UITapGestureRecognizer导致应用崩溃

ios - dispatch_group_notify 在所有任务完成之前被调用