iOS - 使用 MagicalRecord 管理两个 CoreData 模型
iOS - Managing two CoreData models with MagicalRecord
我正在使用 MagicalRecord 来处理 CoreData 模型,该模型可能会在将来进行版本控制。
现在我需要在我的应用程序中添加一个预填充的数据库,其中一个实体包含大约 80000 个对象;此数据是静态的,我预计它不会改变。
如果我将这个实体添加到现有模型中,每次模型更改时我都需要生成一个新的种子数据库,从而增加项目的复杂性。
更好的解决方案是为新实体创建第二个模型:种子数据库永远不会改变,第一个模型可以负责其版本控制,而不管新模型如何。不需要两个模型之间的关系。
在现有模型的基础上,我也在使用 RestKit,这是所有设置的方式:
[MagicalRecord setupAutoMigratingCoreDataStack];
RKManagedObjectStore *managedObjectStore =
[[RKManagedObjectStore alloc] initWithPersistentStoreCoordinator:
[NSPersistentStoreCoordinator MR_newPersistentStoreCoordinator]];
self.objectManager.managedObjectStore = managedObjectStore;
[managedObjectStore createManagedObjectContexts];
// bind RK with MagicalRecord
[NSManagedObjectContext MR_setRootSavingContext:
managedObjectStore.persistentStoreManagedObjectContext];
[NSManagedObjectContext MR_setDefaultContext:
managedObjectStore.mainQueueManagedObjectContext];
managedObjectStore.managedObjectCache = [[RKFetchRequestManagedObjectCache alloc] init];
新模型将不会与 RestKit 一起使用。
这对 MagicalRecord 可行吗?
我已经阅读了它的文档,但能找到任何有用的东西。
非常感谢,
丹
更新
让我们使用 xcode 编辑器创建一个包含 4 个实体(Foo、Bar、Blarg、Baz)的数据库模型。
模型编辑器有一个无法删除的默认配置,所以我们只能添加两个新配置(SeedConfiguration 和 UserConfiguration),将 Foo 添加到第一个,将其他三个添加到第二个。
这两个配置应该保存在seed.sqlite和user.sqlite中。
在这一点上,我想要 运行 一个脚本,它用数千个 Foo 对象填充 seed.sqlite :一旦生成,这个文件将被放入项目资源中,并在启动时复制到应用程序目录中; user.sqlite 将在 运行 时生成并用于管理用户信息。
当我以 "script" 模式启动应用程序以填充 seed.sqlite 时,正确创建了两个 sqlite 文件,但它们都包含所有实体,而我希望在 seed.sqlite 和 user.sqlite.
中的 Bar、Blarg、Baz
即使它包含所有其他(空)实体,我是否应该插入 Foo 对象并复制结果 seed.sqlite?
以下是如何在一个协调器中创建两个持久性存储:
为了澄清起见,如果我可以只有一个 sqlite 文件就好了,但是这样做我必须在每次模型更改时生成种子数据库。
我不想回答太长,因为我不使用 MagicalRecord,而且我不知道它如何管理模型配置。
也就是说,您想要解决此问题的方法是使用模型配置和多个存储文件。这个问题既容易理解又有据可查。
Apple's documentation is a good starting point, and there are numerous articles and examples on the web.
编辑
好的 DAN,这是一个使用多个配置的有点人为(但简单)的示例。您应该能够 copy/paste 将其放入测试文件并 运行 它,这应该允许您跟踪正在发生的事情并获得基本的理解。
请注意,这不是我建议编写生产代码或测试的方式(我也不建议忽略错误),但我希望这有助于解释一些事情并允许您进行实验。
我将代码分成几个辅助方法,希望能更好地解释。
首先,让我们创建一个简单的模型,其中包含四个实体,我们将在每个配置中放入两个实体。
- (NSManagedObjectModel *)makeConfigurationModel {
NSAttributeDescription *nameAttr = [[NSAttributeDescription alloc] init];
nameAttr.name = @"name";
nameAttr.attributeType = NSStringAttributeType;
NSEntityDescription *foo = [[NSEntityDescription alloc] init];
foo.name = @"Foo";
foo.properties = @[[nameAttr copy]];
NSEntityDescription *bar = [[NSEntityDescription alloc] init];
bar.name = @"Bar";
bar.properties = @[[nameAttr copy]];
NSEntityDescription *blarg = [[NSEntityDescription alloc] init];
blarg.name = @"Blarg";
blarg.properties = @[[nameAttr copy]];
NSEntityDescription *baz = [[NSEntityDescription alloc] init];
baz.name = @"Baz";
baz.properties = @[[nameAttr copy]];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] init];
model.entities = @[foo, bar, blarg, baz];
[model setEntities:@[foo, bar] forConfiguration:@"One"];
[model setEntities:@[blarg, baz] forConfiguration:@"Two"];
return model;
}
接下来,将两个商店分配给 PSC 并创建一些示例实体的函数。此功能还检查以确保可以访问所有实体。
- (void)setupDatabaseWithModel:(NSManagedObjectModel*)model
store1:(NSURL*)store1URL
store2:(NSURL*)store2URL {
@autoreleasepool {
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:model];
[psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:@"One"
URL:store1URL
options:nil
error:NULL];
[psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:@"Two"
URL:store2URL
options:nil
error:NULL];
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc]
initWithConcurrencyType:NSMainQueueConcurrencyType];
moc.persistentStoreCoordinator = psc;
// Add some entities...
NSArray *entityNames = @[@"Foo", @"Bar", @"Blarg", @"Baz"];
for (NSString *e in entityNames) {
NSManagedObject *obj =
[NSEntityDescription insertNewObjectForEntityForName:e
inManagedObjectContext:moc];
[obj setValue:[NSString stringWithFormat:@"%@ 1", e] forKey:@"name"];
}
[moc save:NULL];
// Should have all of them in this MOC...
for (NSString *e in entityNames) {
NSFetchRequest *fetchRequest = [NSFetchRequest
fetchRequestWithEntityName:e];
NSArray *result = [moc executeFetchRequest:fetchRequest error:NULL];
XCTAssertEqual(1, result.count);
NSManagedObject *obj = [result firstObject];
XCTAssertEqualObjects(([NSString stringWithFormat:@"%@ 1", e]),
[obj valueForKey:@"name"]);
}
}
}
以及检查某些实体是否在商店中的功能。
- (void)checkStore:(NSURL*)storeURL
model:(NSManagedObjectModel*)model
present:(NSArray*)present
notPresent:(NSArray*)notPresent {
@autoreleasepool {
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:model];
[psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:nil
error:NULL];
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc]
initWithConcurrencyType:NSMainQueueConcurrencyType];
moc.persistentStoreCoordinator = psc;
for (NSString *e in present) {
NSFetchRequest *fetchRequest = [NSFetchRequest
fetchRequestWithEntityName:e];
NSArray *result = [moc executeFetchRequest:fetchRequest error:NULL];
XCTAssertEqual(1, result.count);
NSManagedObject *obj = [result firstObject];
XCTAssertEqualObjects(([NSString stringWithFormat:@"%@ 1", e]),
[obj valueForKey:@"name"]);
}
for (NSString *e in notPresent) {
NSFetchRequest *fetchRequest = [NSFetchRequest
fetchRequestWithEntityName:e];
NSArray *result = [moc executeFetchRequest:fetchRequest error:NULL];
XCTAssertEqual(0, result.count);
}
}
}
还有一个删除 URL
的小帮手
static void removeURL(NSURL ** url) {
[[NSFileManager defaultManager] removeItemAtURL:*url error:NULL];
}
还有一个测试函数...
- (void)testConfigurations {
__attribute__((cleanup(removeURL))) NSURL * __autoreleasing dirURL =
[[[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:YES
error:NULL]
URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
[[NSFileManager defaultManager] createDirectoryAtURL:dirURL
withIntermediateDirectories:YES
attributes:nil
error:NULL];
NSManagedObjectModel *model = [self makeConfigurationModel];
NSURL *store1URL = [dirURL URLByAppendingPathComponent:@"store1"];
NSURL *store2URL = [dirURL URLByAppendingPathComponent:@"store2"];
[self setupDatabaseWithModel:model store1:store1URL store2:store2URL];
[self checkStore:store1URL
model:model
present:@[@"Foo", @"Bar"]
notPresent:@[@"Blarg", @"Baz"]];
[self checkStore:store2URL
model:model
present:@[@"Blarg", @"Baz"]
notPresent:@[@"Foo", @"Bar"]];
}
我正在使用 MagicalRecord 来处理 CoreData 模型,该模型可能会在将来进行版本控制。
现在我需要在我的应用程序中添加一个预填充的数据库,其中一个实体包含大约 80000 个对象;此数据是静态的,我预计它不会改变。
如果我将这个实体添加到现有模型中,每次模型更改时我都需要生成一个新的种子数据库,从而增加项目的复杂性。
更好的解决方案是为新实体创建第二个模型:种子数据库永远不会改变,第一个模型可以负责其版本控制,而不管新模型如何。不需要两个模型之间的关系。
在现有模型的基础上,我也在使用 RestKit,这是所有设置的方式:
[MagicalRecord setupAutoMigratingCoreDataStack];
RKManagedObjectStore *managedObjectStore =
[[RKManagedObjectStore alloc] initWithPersistentStoreCoordinator:
[NSPersistentStoreCoordinator MR_newPersistentStoreCoordinator]];
self.objectManager.managedObjectStore = managedObjectStore;
[managedObjectStore createManagedObjectContexts];
// bind RK with MagicalRecord
[NSManagedObjectContext MR_setRootSavingContext:
managedObjectStore.persistentStoreManagedObjectContext];
[NSManagedObjectContext MR_setDefaultContext:
managedObjectStore.mainQueueManagedObjectContext];
managedObjectStore.managedObjectCache = [[RKFetchRequestManagedObjectCache alloc] init];
新模型将不会与 RestKit 一起使用。
这对 MagicalRecord 可行吗? 我已经阅读了它的文档,但能找到任何有用的东西。
非常感谢, 丹
更新
让我们使用 xcode 编辑器创建一个包含 4 个实体(Foo、Bar、Blarg、Baz)的数据库模型。 模型编辑器有一个无法删除的默认配置,所以我们只能添加两个新配置(SeedConfiguration 和 UserConfiguration),将 Foo 添加到第一个,将其他三个添加到第二个。 这两个配置应该保存在seed.sqlite和user.sqlite中。 在这一点上,我想要 运行 一个脚本,它用数千个 Foo 对象填充 seed.sqlite :一旦生成,这个文件将被放入项目资源中,并在启动时复制到应用程序目录中; user.sqlite 将在 运行 时生成并用于管理用户信息。
当我以 "script" 模式启动应用程序以填充 seed.sqlite 时,正确创建了两个 sqlite 文件,但它们都包含所有实体,而我希望在 seed.sqlite 和 user.sqlite.
中的 Bar、Blarg、Baz即使它包含所有其他(空)实体,我是否应该插入 Foo 对象并复制结果 seed.sqlite?
以下是如何在一个协调器中创建两个持久性存储:
为了澄清起见,如果我可以只有一个 sqlite 文件就好了,但是这样做我必须在每次模型更改时生成种子数据库。
我不想回答太长,因为我不使用 MagicalRecord,而且我不知道它如何管理模型配置。
也就是说,您想要解决此问题的方法是使用模型配置和多个存储文件。这个问题既容易理解又有据可查。
Apple's documentation is a good starting point, and there are numerous articles and examples on the web.
编辑
好的 DAN,这是一个使用多个配置的有点人为(但简单)的示例。您应该能够 copy/paste 将其放入测试文件并 运行 它,这应该允许您跟踪正在发生的事情并获得基本的理解。
请注意,这不是我建议编写生产代码或测试的方式(我也不建议忽略错误),但我希望这有助于解释一些事情并允许您进行实验。
我将代码分成几个辅助方法,希望能更好地解释。
首先,让我们创建一个简单的模型,其中包含四个实体,我们将在每个配置中放入两个实体。
- (NSManagedObjectModel *)makeConfigurationModel {
NSAttributeDescription *nameAttr = [[NSAttributeDescription alloc] init];
nameAttr.name = @"name";
nameAttr.attributeType = NSStringAttributeType;
NSEntityDescription *foo = [[NSEntityDescription alloc] init];
foo.name = @"Foo";
foo.properties = @[[nameAttr copy]];
NSEntityDescription *bar = [[NSEntityDescription alloc] init];
bar.name = @"Bar";
bar.properties = @[[nameAttr copy]];
NSEntityDescription *blarg = [[NSEntityDescription alloc] init];
blarg.name = @"Blarg";
blarg.properties = @[[nameAttr copy]];
NSEntityDescription *baz = [[NSEntityDescription alloc] init];
baz.name = @"Baz";
baz.properties = @[[nameAttr copy]];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] init];
model.entities = @[foo, bar, blarg, baz];
[model setEntities:@[foo, bar] forConfiguration:@"One"];
[model setEntities:@[blarg, baz] forConfiguration:@"Two"];
return model;
}
接下来,将两个商店分配给 PSC 并创建一些示例实体的函数。此功能还检查以确保可以访问所有实体。
- (void)setupDatabaseWithModel:(NSManagedObjectModel*)model
store1:(NSURL*)store1URL
store2:(NSURL*)store2URL {
@autoreleasepool {
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:model];
[psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:@"One"
URL:store1URL
options:nil
error:NULL];
[psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:@"Two"
URL:store2URL
options:nil
error:NULL];
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc]
initWithConcurrencyType:NSMainQueueConcurrencyType];
moc.persistentStoreCoordinator = psc;
// Add some entities...
NSArray *entityNames = @[@"Foo", @"Bar", @"Blarg", @"Baz"];
for (NSString *e in entityNames) {
NSManagedObject *obj =
[NSEntityDescription insertNewObjectForEntityForName:e
inManagedObjectContext:moc];
[obj setValue:[NSString stringWithFormat:@"%@ 1", e] forKey:@"name"];
}
[moc save:NULL];
// Should have all of them in this MOC...
for (NSString *e in entityNames) {
NSFetchRequest *fetchRequest = [NSFetchRequest
fetchRequestWithEntityName:e];
NSArray *result = [moc executeFetchRequest:fetchRequest error:NULL];
XCTAssertEqual(1, result.count);
NSManagedObject *obj = [result firstObject];
XCTAssertEqualObjects(([NSString stringWithFormat:@"%@ 1", e]),
[obj valueForKey:@"name"]);
}
}
}
以及检查某些实体是否在商店中的功能。
- (void)checkStore:(NSURL*)storeURL
model:(NSManagedObjectModel*)model
present:(NSArray*)present
notPresent:(NSArray*)notPresent {
@autoreleasepool {
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:model];
[psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:nil
error:NULL];
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc]
initWithConcurrencyType:NSMainQueueConcurrencyType];
moc.persistentStoreCoordinator = psc;
for (NSString *e in present) {
NSFetchRequest *fetchRequest = [NSFetchRequest
fetchRequestWithEntityName:e];
NSArray *result = [moc executeFetchRequest:fetchRequest error:NULL];
XCTAssertEqual(1, result.count);
NSManagedObject *obj = [result firstObject];
XCTAssertEqualObjects(([NSString stringWithFormat:@"%@ 1", e]),
[obj valueForKey:@"name"]);
}
for (NSString *e in notPresent) {
NSFetchRequest *fetchRequest = [NSFetchRequest
fetchRequestWithEntityName:e];
NSArray *result = [moc executeFetchRequest:fetchRequest error:NULL];
XCTAssertEqual(0, result.count);
}
}
}
还有一个删除 URL
的小帮手static void removeURL(NSURL ** url) {
[[NSFileManager defaultManager] removeItemAtURL:*url error:NULL];
}
还有一个测试函数...
- (void)testConfigurations {
__attribute__((cleanup(removeURL))) NSURL * __autoreleasing dirURL =
[[[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:YES
error:NULL]
URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
[[NSFileManager defaultManager] createDirectoryAtURL:dirURL
withIntermediateDirectories:YES
attributes:nil
error:NULL];
NSManagedObjectModel *model = [self makeConfigurationModel];
NSURL *store1URL = [dirURL URLByAppendingPathComponent:@"store1"];
NSURL *store2URL = [dirURL URLByAppendingPathComponent:@"store2"];
[self setupDatabaseWithModel:model store1:store1URL store2:store2URL];
[self checkStore:store1URL
model:model
present:@[@"Foo", @"Bar"]
notPresent:@[@"Blarg", @"Baz"]];
[self checkStore:store2URL
model:model
present:@[@"Blarg", @"Baz"]
notPresent:@[@"Foo", @"Bar"]];
}