最佳答案
我有一个大的导入任务,我需要做的核心数据。
假设我的核心数据模型是这样的:
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,所以我的代码看起来像这样:
这是 AppCommittee 中的核心数据配置:
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;
}
这是我的 import 方法现在的样子:
- (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];
}];
}
}
}];