我已经在 App Store 中有一个使用核心数据来保存数据的应用程序。
现在,当 iOS 8 即将发布时,我想为其添加一个小部件,因此我必须使用应用程序组在二进制文件之间共享数据。
但有一个问题 - 我需要更改商店位置以支持所有现有用户的应用程序组。
我编写了以下代码,试图将商店移动到新路径:
// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSURL *oldStoreURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
oldStoreURL = [oldStoreURL URLByAppendingPathComponent:@"Schooler.sqlite"];
NSURL *storeURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.com.schooler.mycontainer"];
storeURL = [storeURL URLByAppendingPathComponent:@"Schooler.sqlite"];
if([[NSFileManager defaultManager] fileExistsAtPath:oldStoreURL.path] == YES && [[NSFileManager defaultManager] fileExistsAtPath:storeURL.path] == NO)
{
// Prior today extension - Need to move to new directory
NSError *error = nil;
if([[NSFileManager defaultManager] moveItemAtURL:oldStoreURL toURL:storeURL error:&error] == YES)
NSLog(@"Migrated successfully to new database location.");
else
NSLog(@"error: %@",error);
}
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
}
return _persistentStoreCoordinator;
}
输出始终是“已成功迁移到新的数据库位置。”,尽管之前保存在应用程序上的所有数据都已被删除,就好像它创建了一个新数据库而不是仅仅移动它一样。
造成问题的原因是什么?我应该如何修复它?
谢谢你。
使用默认选项创建的 Core Data NSSQLiteStoreType 存储实际上是多个文件,如技术问答 1809:iOS 7 和 OS X Mavericks 中 Core Data SQLite 存储的新默认日志模式中所述。当尝试将存储移动到迁移过程之外时,记住这一点很重要,这也是问题的根源 - 当您需要移动所有文件时,您正在移动一个文件。但是,不建议在没有文件协调器的情况下将文件单独移到 Core Data 之外。最好使用迁移来代替。 迁移将从源存储中获取数据并将其迁移到新的存储位置,实质上是在新位置复制旧数据。旧数据仍将存在于文件系统上。在您的应用程序中,您应该像现在一样执行迁移,但不要尝试自己将旧数据移动到新位置 - 这就是出现问题的地方。
您可以依靠迁移来为您移动数据,而不是自己移动文件。首先,使用源数据的 URL 将存储添加到持久存储协调器。然后您将执行迁移以将该数据移动到新 URL
NSPersistentStore *sourceStore = nil;
NSPersistentStore *destinationStore = nil;
NSDictionary *storeOptions = @{ NSSQLitePragmasOption : @{ @"journal_mode" :
@"WAL" } };
// Add the source store
if (![coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:oldStoreURL options:storeOptions error:&error]){
// Handle the error
} else {
sourceStore = [coordinator persistentStoreForURL:oldStoreURL];
if (sourceStore != nil){
// Perform the migration
destinationStore = [coordinator migratePersistentStore:sourceStore toURL:storeURL options:storeOptions withType:NSSQLiteStoreType error:&error];
if (destinationStore == nil){
// Handle the migration error
} else {
// You can now remove the old data at oldStoreURL
// Note that you should do this using the NSFileCoordinator/NSFilePresenter APIs, and you should remove the other files
// described in QA1809 as well.
}
}
}
迁移完成后,您可以删除旧文件。这里的示例明确指定了 SQLite 日志选项,这是为了确保如果将来更改默认选项,代码仍然可以工作。如果您使用不同的选项,则应该使用这些选项。
let oldPersistentStoreURL: URL = ...
let sharedPersistentStoreURL: URL = ...
let options = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true] // + any database-specific options
if FileManager.default.fileExists(atPath: oldPersistentStoreURL.path) {
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
do {
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: oldPersistentStoreURL, options: options)
if let sourceStore = coordinator.persistentStore(for: oldPersistentStoreURL) {
let _ = try coordinator.migratePersistentStore(sourceStore, to: sharedPersistentStoreURL, options: options, withType: NSSQLiteStoreType)
// If migration was successful then delete the old files
}
} catch {
error.logErrors()
}
}