我正在使用嵌套上下文模式来支持 CoreData 的多线程工作。 我有 CoredDataManager 单例类,上下文的初始化是:
self.masterContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
self.masterContext.persistentStoreCoordinator = self.persistentStoreCoordinator;
self.mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
self.mainContext.parentContext = self.masterContext;
对于来自 Web 服务的响应的每个插入操作,我使用我的 CoreDataManager 的 API 来获取新的托管上下文:
- (NSManagedObjectContext *)newManagedObjectContext {
NSManagedObjectContext *workerContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
workerContext.parentContext = self.mainContext;
return workerContext;
}
它看起来像(PlayerView 类是 NSManagedObject 类的子类):
[PlayerView insertIfNeededByUniqueKey:@"playerViewId" value:playerViewId inBackgroundWithCompletionBlock:^(NSManagedObjectContext *context, PlayerView *playerView) {
playerView.playerViewId = playerViewId;
playerView.username = playerViewDictionary[@"name"];
[context saveContextWithCompletionBlock:^{
//do something
} onMainThread:NO];//block invocation on background thread
}];
saveContextWithCompletionBlock 方法在 NSManagedObjectContext 类别中实现:
- (void)saveContextWithCompletionBlock:(SaveContextBlock)completionBlock onMainThread:(BOOL)onMainThread {
__block NSError *error = nil;
if (self.hasChanges) {
[self performBlock:^{
[self save:&error];
if (error) {
@throw [NSException exceptionWithName:NSUndefinedKeyException
reason:[NSString stringWithFormat:@"Context saving error: %@\n%@\n%@", error.domain, error.description, error.userInfo]
userInfo:error.userInfo];
}
if (completionBlock) {
if (onMainThread && [NSThread isMainThread]) {
completionBlock();
} else if (onMainThread) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock();
});
} else if ([NSThread isMainThread]) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul), ^{
completionBlock();
});
} else {
completionBlock();
}
}
}];
}
}
然后在某个阶段我调用 CoreDataManager 的方法来保存主上下文:
- (void)saveMasterContext; {
__block NSError *error;
[self.mainContext performBlock:^{
[self.mainContext save:&error];
[self treatError:error];
[self.masterContext performBlock:^{
[self.masterContext save:&error];
[self treatError:error];
}];
}];
}
我有两个主要类,NSManagedObject 的子类 - PlayerView 和 Post。 PlayerView 与 Post 具有一对多的关系。 PlayerView 已经保存好了。帖子永远不会保存,我收到错误消息:
CoreData:错误:从上下文中删除托管对象 0x17dadd80 (0x17daf930) 后对其进行变异。
我认为,问题在于上下文保存逻辑。
最佳答案
首先,您遇到的错误通常发生在您创建新托管对象的上下文在您有机会保存它之前消失(释放)时。
其次,确保上下文保存在适当线程中的最佳方法是使用 performBlock
或 performBlockAndWait
而不是试图找出上下文属于哪个线程至。这是一个安全保存上下文的示例“保存”函数:
+ (BOOL)save:(NSManagedObjectContext *)context {
__block BOOL saved = NO;
[context performBlockAndWait: {
NSError *error;
saved = [context save:&error];
if (!saved) {
NSLog("failed to save: %@", error);
}
}]
return saved;
}
至于使用嵌套私有(private)上下文(主线程上下文作为父上下文),我们的团队遇到了该模型的一些问题(不记得到底是什么),但我们决定监听 NSManagedObjectContextDidSaveNotification
并使用 mergeChangesFromContextDidSaveNotification
更新上下文。
希望对您有所帮助。
关于ios - CoreData 嵌套上下文 : what is the proper way to save context?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22064878/