我们的应用需要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:
中?
在使用轻量级迁移时,甚至可以做我想做的事情吗?
答案 0 :(得分:2)
加载持久性存储时会发生迁移。使用NSPersistentContainer
时,只要您拨打loadPersistentStores()
,就会发生这种情况。在您的代码中,它看起来可能是ZSSCoreDataManager
的{{1}}方法。
这不是关于您执行此操作的方法,而是您调用它的队列的问题。如果您长时间阻止主队列,看门狗将终止您的应用程序。当您这样做时,您的应用程序已停止响应。看门狗无法判断为什么会发生这种情况,它强制执行的规则是将其视为挂起进程并将其终止。这通常是一个好主意,因为它只发生在你的应用程序长时间忽略用户输入的情况下。如果你在persistentContainer
处理迁移,那么你就在主队列中,这就是你的应用被杀的原因。
我不确定这是否只能通过viewDidLoad
修复。该课程旨在处理最常见的情况,但你似乎在那之外。在过去,我所做的是使用NSPersistentContainer
在后台队列上加载持久性存储,然后创建我需要的任何托管对象上下文。您可以通过首先使用NSPersistentStoreCoordinator
来处理迁移,然后在完成时加载NSPersistentStoreCoordinator
来处理此问题。
修复此问题部分取决于您是否可以重现崩溃。如果您现在不能这样做,那可能是第一步 - 到达您看到与用户相同的崩溃的位置,以便您可以判断崩溃何时得到解决。