我有一个带有大型预加载数据库和小型用户数据库(两个 CoreData SQLite 存储)的 iOS 项目。以前的问题建议使用配置来控制哪些实体与哪个商店一起使用。我很难让它发挥作用。这是我一直在尝试的...
- (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];
这工作正常,返回适当命名的 Category 对象,直到我取消注释添加第二个商店。如果我这样做,则获取结果会返回为空。诊断 NSLog 消息完全符合我的预期。每个商店都与正确的配置相关联,并且每个配置都有相应的实体。
谁能指出我在多个商店设置中的源代码,或者让我知道我做错了什么?提前致谢!
已解决:问题的症结在于第一个代码清单中标记为 WRONG 的两行。我试图以编程方式创建配置,但这似乎还不够。如果您在执行此操作后查询 ManagedObjectModel 的配置,您确实会在列表中看到配置,并且正确的实体与这些配置相关联。但是,似乎还需要做一些其他事情才能使 PersistentStoreCoordinator 能够正确使用它们。在 Xcode 中创建配置使它们工作。
跟进:有一个额外的障碍。在设置最终的 Persistent Store Coordinator 之前运行单独的迁移通道的解决方案效果很好......在模拟器中。在实际设备上,权限更严格。如果您尝试执行该迁移,则会失败,因为 App 包中的存储是只读的。除非您整合模型,否则迁移似乎是必要的。如果您只有一个模型,并且 App 包中的商店与其兼容,则不需要迁移,并且使用 Xcode 中定义的配置进行访问是可行的。
另一种选择可能是在尝试迁移之前将数据移动到 Documents 目录中。我还没有验证这种方法是否有效。