[英]Core Data - lightweight migrations and multiple core data model files (xcdatamodel)
从由两个单独的xcdatamodel文件定义的商店进行迁移时,执行轻量级迁移时遇到问题。
在应用程序的1.0版中,我将模型分为分析模型,模型A和模型B中的所有其他模型。 编译时,模型将被分组在一起,并且一切都进行顺利。
在使用新版本1.1时,我通过向模型B添加新模型版本并将该新版本设置为活动版本来升级模型B。
从1.0升级到1.1时会出现问题。 似乎Core Data检查了磁盘上的模型存储(由1.0版创建),并寻找描述它的模型,但无法找到定义整个存储的SINGLE模型(模型A仅涵盖分析,模型B涵盖其他所有内容),因此会引发“找不到源存储的模型”错误。
有没有人找到一种解决方案,用于分离模型,但仍允许升级+轻量级迁移工作而无需定义自定义迁移的额外麻烦?
这是用于加载模型的代码片段:
NSArray *modelNames = [NSArray arrayWithObjects:@"model-A", @"model-B", nil];
NSMutableArray *models = [NSMutableArray array];
for (NSString *name in modelNames)
{
LogInfo(@"loading model %@", name);
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:name withExtension:@"momd"];
NSManagedObjectModel *model = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] autorelease];
[models addObject:model];
}
// combine all the separate models into one big one
objectModel = [[NSManagedObjectModel modelByMergingModels:models] retain];
NSURL *documentsDirectory = [NSURL fileURLWithPath:[SuperFileManager documentsDirectory] isDirectory:YES];
NSURL *storeURL = [documentsDirectory URLByAppendingPathComponent:@"database.sqlite"];
NSError *error = nil;
coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:objectModel];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,
nil];
if (![coordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:options
error:&error])
{
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
参加WWDC 2012实验室并与Core Data团队会面后,似乎您被迫将所有模型信息都放在一个xcdatamodel中。 CoreData不够智能,无法将其现有存储作为创建它并仍在磁盘上的存储的组合进行检查。 正如C. Roald指出的那样,您可以对旧的xcdatamodel文件进行一些处理,但是令人遗憾的是Core Data不能更优雅地处理这一问题。
我也遇到了这个问题。 我花了几个小时试图弄清楚WTF-非常沮丧。
我相信解决此问题的最简单方法是:
选择要保留的模型(例如ModelB),然后根据发布的版本为其创建新版本。 我将其称为已发布版本ModelBv1和新版本ModelBv1_merge。
在文本编辑器中打开ModelAv1和ModelBv1_merge的内容XML文件(即ModelA.xcdatamodeld/ModelAv1.xcdatamodel/contents
和ModelB.xcdatamodeld/ModelBv1_merge.xcdatamodel/contents
),并手动合并XML。 模式非常简单-只需复制<entity>
元素并合并<elements>
元素(到_merge内容文件中),就可以完成。
打开新ModelBv2的内容文件,然后再次将ModelA内容合并到其中。
从您的项目文件中删除ModelA。
在Xcode中检查ModelBv1_merge和ModelBv2看起来是否健全,并包含您期望的所有内容(旧模型A和模型B的结合)。 构建,您应该完成。
(我认为这是“如果两个内容文件都是由相同版本的Xcode编写的”,但我认为如果您使用的是旧的内容文件,则可以通过在某个地方进行小改动使Xcode重写它,应该很容易。 )
我有一个场景,其中我的应用程序模型是通过合并多个模型而获得的,并且我设法通过这种方式实现了一种自动的轻量级迁移:
NSError* error = nil;
NSURL *documentsDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *storeURL = [documentsDirectory URLByAppendingPathComponent:@"db.sqlite"];
NSString* storePath = [storeURL path];
NSLog(@"Store URL: %@", storeURL);
if( [[NSFileManager defaultManager] fileExistsAtPath:storePath] ){
// Load store metadata (this will contain information about the versions of the models this store was created with)
NSDictionary *storeMeta = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:nil URL:storeURL error:&error];
if(storeMeta){
// Get the current model, merging all the models in the main bundle (in their current version)
NSManagedObjectModel* model=[NSManagedObjectModel mergedModelFromBundles:nil];
// If the persistent store is not compatible with such a model (i.e. it was created with a model obtained merging old versions of "submodels"), migrate
if(![model isConfiguration:nil compatibleWithStoreMetadata:storeMeta]){
// Load the old model
NSManagedObjectModel*oldModel = [NSManagedObjectModel mergedModelFromBundles:nil forStoreMetadata:storeMeta];
// Compute the mapping between old model and new model
NSMappingModel* mapping = [NSMappingModel inferredMappingModelForSourceModel:oldModel destinationModel:model error:&error];
if(mapping){
// Backup old store
NSURL* storeBackupURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:[NSString stringWithFormat:@"db.sqlite.%@.bck", [NSDate new]]];
BOOL done = [[NSFileManager defaultManager] moveItemAtURL:storeURL toURL:storeBackupURL error:&error];
if(done){
// Apply the mapping
NSMigrationManager* migrationManager = [[NSMigrationManager alloc] initWithSourceModel:oldModel destinationModel:model];
BOOL done = [migrationManager migrateStoreFromURL: storeBackupURL
type: NSSQLiteStoreType
options: nil
withMappingModel: mapping
toDestinationURL: storeURL
destinationType: NSSQLiteStoreType
destinationOptions: nil
error: &error];
if(done){
NSLog(@"Store migration successful!!!");
}
}
}
}
}
}
if(error){
NSLog(@"Migration error: %@", error);
}
升级核心数据模型的最佳方法是添加一个版本。 如果不这样做,您将死于崩溃,更新风险和类似的东西。
实际上,添加新版本非常容易。 您选择数据模型文件,然后选择“编辑器>添加模型版本”。 这将使您能够基于以前的模型添加新的数据库版本。 然后,您需要将当前数据模型设置为最新的: http : //cl.ly/2h1g301b0N143t0b1k2K
安装新版本后,iO将自动迁移数据(至少在我的情况下)。
希望这可以帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.