【发布时间】:2012-04-30 17:28:09
【问题描述】:
我有一个 iOS 项目,其中包含一个大型预加载数据库和一个小型用户数据库(两个都是 CoreData SQLite 存储)。以前的问题建议使用配置来控制哪些实体与哪个商店一起使用。我很难让它发挥作用。这是我一直在尝试的...
- (NSManagedObjectModel *)managedObjectModel
{
if (_managedObjectModel != nil) return _managedObjectModel;
// set up the model for the preloaded data
NSURL *itemURL = [[NSBundle mainBundle] URLForResource:@"FlagDB" withExtension:@"momd"];
NSManagedObjectModel *itemModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:itemURL];
// set up the model for the user data
NSURL *userDataURL = [[NSBundle mainBundle] URLForResource:@"UserData" withExtension:@"momd"];
NSManagedObjectModel *userDataModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:userDataURL];
// merge the models
_managedObjectModel = [NSManagedObjectModel modelByMergingModels:[NSArray arrayWithObjects:itemModel, userDataModel, nil]];
// define configurations based on what was in each model
WRONG [_managedObjectModel setEntities:itemModel.entities forConfiguration:@"ItemData"];
WRONG [_managedObjectModel setEntities:userDataModel.entities forConfiguration:@"UserData"];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil) return _persistentStoreCoordinator;
// preloaded data is inside the bundle
NSURL *itemURL = [[[NSBundle mainBundle] bundleURL] URLByAppendingPathComponent:@"FlagDB.sqlite"];
// user data is in the application directory
NSURL *userDataURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"UserData.sqlite"];
NSManagedObjectModel *mom = self.managedObjectModel;
NSError *error = nil;
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:@"ItemData" URL:itemURL options:nil error:&error])
{
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
...
这会因“用于打开商店的模型与用于创建商店的模型不兼容”而中止。检查模型中的哈希值与商店中的哈希值表明它们对于 ItemData 配置中的实体是相同的。
如果我尝试进行轻量级迁移,如下所示:
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:@"ItemData" URL:itemURL options:options error:&error])
它因“NSInvalidArgumentException”而失败,原因:“模型不包含配置“ItemData”。我认为这是因为轻量级迁移过程正在创建一个新模型,并且它不包含我的配置。
根据其他线程中的一些建议,我尝试在没有配置的情况下进行轻量级迁移,然后使用配置创建新的协调器。这种工作,但它添加表到我的预加载 .sqlite 文件对应于用户数据实体(不属于那里),并在新创建的用户数据存储中创建预加载的数据表和用户数据表.最终结果是提取失败,似乎是因为他们在错误的商店中寻找。
NSDictionary *migrationOptions = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
// make a temp persistent store coordinator to handle the migration
NSPersistentStoreCoordinator *tempPsc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
// migrate the stores
if (![tempPsc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:itemURL options:migrationOptions error:&error])
{
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
if (![tempPsc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:userDataURL options:migrationOptions error:&error])
{
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
// make a permanent store coordinator
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
NSDictionary *readOnlyOptions = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSReadOnlyPersistentStoreOption, nil];
if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:@"ItemData" URL:itemURL options:readOnlyOptions error:&error])
{
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
/*if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:@"UserData" URL:userDataURL options:nil error:&error])
{
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}*/
后来……
OSAppDelegate *delegate = [UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = delegate.managedObjectContext;
// sanity check
for (NSPersistentStore *store in context.persistentStoreCoordinator.persistentStores) {
NSLog(@"store %@ -> %@", store.configurationName, store.URL);
NSMutableArray *entityNames = [[NSMutableArray alloc] init];
for (NSEntityDescription *entity in [context.persistentStoreCoordinator.managedObjectModel entitiesForConfiguration:store.configurationName]) {
[entityNames addObject:entity.name];
}
NSLog(@"entities: %@", entityNames);
}
NSFetchRequest *categoryFetchRequest = [[NSFetchRequest alloc] init];
categoryFetchRequest.entity = [NSEntityDescription entityForName:@"Category" inManagedObjectContext:context];
categoryFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", categoryName];
NSError *error = nil;
Category *category = [[delegate.managedObjectContext executeFetchRequest:categoryFetchRequest error:&error] lastObject];
这工作正常,返回适当命名的类别对象,直到我取消注释添加第二个商店。如果我这样做,则获取结果会返回为空。诊断 NSLog 消息完全符合我的预期。每个商店都与正确的配置相关联,并且每个配置都有相应的实体。
谁能指出我在多个商店设置中工作的源代码,或者提示我做错了什么?提前致谢!
已解决:问题的症结在于第一个代码清单中标记为 WRONG 的两行。我试图以编程方式创建配置,但这似乎还不够。如果您在执行此操作后查询 ManagedObjectModel 的配置,您确实确实在列表中看到了配置,并且正确的实体与这些配置相关联。但是,似乎还需要做一些其他事情才能使 PersistentStoreCoordinator 能够正确使用它们。在 Xcode 中创建配置会使它们工作。
跟进:还有一个额外的障碍。在设置最终的 Persistent Store Coordinator 之前运行单独的迁移通道的解决方案效果很好......在模拟器中。在实际设备上,权限更严格。如果您尝试执行该迁移,则会失败,因为 App 包中的存储是只读的。除非您整合模型,否则迁移似乎是必要的。如果您只有一个模型,并且 App 包中的商店与其兼容,则无需迁移,使用 Xcode 中定义的配置访问即可。
另一个选项可能是在尝试迁移之前将数据移动到 Documents 目录中。我尚未验证该方法是否有效。
【问题讨论】:
-
确保您是在应用沙箱的用户文档目录中进行迁移——它是读/写的——而不是在应用程序包本身中。
-
我不想将数据移动到 Documents 目录中,因为我不想备份这些(静态)数据并将其计入用户的 iCloud 配额。但听起来在 iOS 5.0.1 中有一种方法可以指定不备份的文件:developer.apple.com/library/ios/#qa/qa1719/_index.html
-
嗯,你启发了我,在花了几个小时解决我的问题后,我写了一篇关于 here 的完整文章。我认为它可以在未来帮助其他人。
-
看起来不错。您可能会提到的一件事是,如果您的“种子数据”随捆绑包中的应用程序一起提供,则迁移方法不起作用,因为您无法迁移只读存储。
-
没错,这就是为什么我小心地将它从捆绑包移动到文档目录的原因。今晚将改变它,将它移动到缓存目录,这样它就不会被 iCloud 备份。我会确保添加此评论以使其更加明显。
标签: objective-c ios core-data ios5 multiple-databases