【发布时间】:2014-08-30 16:48:57
【问题描述】:
我有一项大型导入任务需要处理核心数据。
假设我的核心数据模型如下所示:
Car
----
identifier
type
我从我的服务器获取汽车信息 JSON 列表,然后我想将它与我的核心数据 Car 对象同步,意思是:
如果是新车 -> 根据新信息创建一个新的核心数据 Car 对象。
如果汽车已经存在 -> 更新核心数据Car 对象。
所以我想在不阻塞 UI 的情况下在后台进行此导入,同时使用滚动显示所有汽车的汽车表视图。
目前我正在做这样的事情:
// create background context
NSManagedObjectContext *bgContext = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[bgContext setParentContext:self.mainContext];
[bgContext performBlock:^{
NSArray *newCarsInfo = [self fetchNewCarInfoFromServer];
// import the new data to Core Data...
// I'm trying to do an efficient import here,
// with few fetches as I can, and in batches
for (... num of batches ...) {
// do batch import...
// save bg context in the end of each batch
[bgContext save:&error];
}
// when all import batches are over I call save on the main context
// save
NSError *error = nil;
[self.mainContext save:&error];
}];
但我不确定我在这里做的是否正确,例如:
我可以使用setParentContext 吗?
我看到了一些这样使用它的示例,但是我看到了其他不调用setParentContext 的示例,而是执行了这样的操作:
NSManagedObjectContext *bgContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
bgContext.persistentStoreCoordinator = self.mainContext.persistentStoreCoordinator;
bgContext.undoManager = nil;
我不确定的另一件事是何时在主上下文中调用 save,在我的示例中,我只是在导入结束时调用 save,但我看到了使用的示例:
[[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification object:nil queue:nil usingBlock:^(NSNotification* note) {
NSManagedObjectContext *moc = self.managedObjectContext;
if (note.object != moc) {
[moc performBlock:^(){
[moc mergeChangesFromContextDidSaveNotification:note];
}];
}
}];
正如我之前提到的,我希望用户能够在更新时与数据进行交互,那么如果我在导入更改同一辆车时用户更改了汽车类型怎么办,我编写它的方式是否安全?
更新:
感谢@TheBasicMind 的精彩解释,我正在尝试实现选项 A,所以我的代码如下所示:
这是 AppDelegate 中的 Core Data 配置:
AppDelegate.m
#pragma mark - Core Data stack
- (void)saveContext {
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
DDLogError(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
}
// main
- (NSManagedObjectContext *)managedObjectContext {
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
_managedObjectContext.parentContext = [self saveManagedObjectContext];
return _managedObjectContext;
}
// save context, parent of main context
- (NSManagedObjectContext *)saveManagedObjectContext {
if (_writerManagedObjectContext != nil) {
return _writerManagedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_writerManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_writerManagedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _writerManagedObjectContext;
}
这就是我的导入方法现在的样子:
- (void)import {
NSManagedObjectContext *saveObjectContext = [AppDelegate saveManagedObjectContext];
// create background context
NSManagedObjectContext *bgContext = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
bgContext.parentContext = saveObjectContext;
[bgContext performBlock:^{
NSArray *newCarsInfo = [self fetchNewCarInfoFromServer];
// import the new data to Core Data...
// I'm trying to do an efficient import here,
// with few fetches as I can, and in batches
for (... num of batches ...) {
// do batch import...
// save bg context in the end of each batch
[bgContext save:&error];
}
// no call here for main save...
// instead use NSManagedObjectContextDidSaveNotification to merge changes
}];
}
而且我还有以下观察者:
[[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification object:nil queue:nil usingBlock:^(NSNotification* note) {
NSManagedObjectContext *mainContext = self.managedObjectContext;
NSManagedObjectContext *otherMoc = note.object;
if (otherMoc.persistentStoreCoordinator == mainContext.persistentStoreCoordinator) {
if (otherMoc != mainContext) {
[mainContext performBlock:^(){
[mainContext mergeChangesFromContextDidSaveNotification:note];
}];
}
}
}];
【问题讨论】:
-
如果您使用父子模型,您必须先保存子节点,然后再保存父节点才能正确更新数据库。
-
这是我的问题的一部分,我应该为这种任务使用父子模型吗?我还有什么其他选择?
-
你应该访问这篇文章你会发现它很有帮助raywenderlich.com/15916/…
-
@cabellicar123 为什么?来自核心数据编程指南:并发:
Once all of the data has been consumed and turned into NSManagedObject instances, you call save on the private context, which moves all of the changes into the main queue context without blocking the main queue.developer.apple.com/library/ios/documentation/Cocoa/Conceptual/… -
@Eyal "我正在尝试在这里进行有效的导入" 它是什么样的?
标签: ios objective-c core-data nsmanagedobjectcontext