Multithreading 核心数据父/子上下文保存失败
我用父/子模型设置了一个背景线程。实际上,上下文保存失败了 这是我的设置。在AppDelegate中,我使用NSMainQueueConcurrencyType设置了_managedObjectContext:Multithreading 核心数据父/子上下文保存失败,multithreading,core-data,Multithreading,Core Data,我用父/子模型设置了一个背景线程。实际上,上下文保存失败了 这是我的设置。在AppDelegate中,我使用NSMainQueueConcurrencyType设置了_managedObjectContext: - (NSManagedObjectContext *)managedObjectContext { if (_managedObjectContext != nil) { return _managedObjectContext; } NSPe
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];//[[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
在我的数据加载类中,我在此处设置父/子MOC以在后台线程上执行工作:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSManagedObjectContext *mainMOC = self.managedObjectContext;
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
[moc setParentContext:mainMOC];
[moc setUndoManager:nil];
dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSManagedObjectContext *mainMOC = self.managedObjectContext;
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
json数据完成后,我尝试使用以下宏执行保存操作:
#define SAVE_MOC { NSError *error; \
if (![moc save:&error]) { NSLog(@"Sub MOC Error"); } \
[mainMOC performBlock:^{ NSError *e = nil; if (![mainMOC save:&e]) {
NSLog(@"Main MOC Error %@",error.localizedDescription);}}];}
同样,当我完成数据加载后,我跳回主线程,如下所示:
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"<---- complete CS sub moc! ---->");
//this fires ok
});
dispatch\u async(dispatch\u get\u main\u queue()^{
NSLog(@“);
//这火还可以
});
因此,从我的SAVE_MOC宏中,我只得到一个简单的错误:
主主运行中心错误(空)
如果我能提供更多信息,请告诉我。我对多线程非常陌生,并试图更好地处理这种方法
谢谢,
乔希
在我的数据加载类中,我在这里设置父/子MOC以执行
后台线程上的工作:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSManagedObjectContext *mainMOC = self.managedObjectContext;
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
[moc setParentContext:mainMOC];
[moc setUndoManager:nil];
dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSManagedObjectContext *mainMOC = self.managedObjectContext;
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
你不应该那样做。改为这样做
NSManagedObjectContext *mainMOC = self.managedObjectContext;
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
确保在performBlock
中访问主运行中心。比如说,
[moc performBlock:^{
// Anything at all involving this MOC or any of its objects
}];
当json数据完成后,我尝试执行一个保存操作
使用以下宏:
#define SAVE_MOC { NSError *error; \
if (![moc save:&error]) { NSLog(@"Sub MOC Error"); } \
[mainMOC performBlock:^{ NSError *e = nil; if (![mainMOC save:&e]) {
NSLog(@"Main MOC Error %@",error.localizedDescription);}}];}
考虑用这样的东西存钱。保存完成后,将调用完成块
- (void)saveMOC:(NSManagedObjectContext*)moc
completion:(void(^)(NSError *error))completion {
[moc performBlock:^{
NSError *error = nil;
if ([moc save:&error]) {
if (moc.parentContext) {
return [self saveMOC:moc.parentContext completion:completion];
}
}
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(error);
});
}
}];
}
[self saveMOC:moc completion:^(NSError *error) {
// Completion handler is called from main-thread, after save has finished
if (error) {
// Handle error
} else {
}
}];
编辑
如果moc.parentContext是主并发类型,则此代码将崩溃
蒙迪
我发布的代码没有内在原因导致父MOC的NSMainQueueConcurrencyType
崩溃。自从将父/子添加到核心数据以来,它一直支持作为父上下文
也许我遗漏了一个输入错误,所以我直接从这个答案复制/粘贴saveMOC:completion:
,并编写了以下测试助手
- (void)testWithChildConcurrencyType:(NSManagedObjectContextConcurrencyType)childConcurrencyType
parentConcurrencyType:(NSManagedObjectContextConcurrencyType)parentConcurrencyType {
NSAttributeDescription *attr = [[NSAttributeDescription alloc] init];
attr.name = @"attribute";
attr.attributeType = NSStringAttributeType;
NSEntityDescription *entity = [[NSEntityDescription alloc] init];
entity.name = @"Entity";
entity.properties = @[attr];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] init];
model.entities = @[entity];
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
[psc addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:NULL];
NSManagedObjectContext *parent = [[NSManagedObjectContext alloc] initWithConcurrencyType:parentConcurrencyType];
parent.persistentStoreCoordinator = psc;
NSManagedObjectContext *child = [[NSManagedObjectContext alloc] initWithConcurrencyType:childConcurrencyType];
child.parentContext = parent;
NSManagedObject *obj = [NSEntityDescription insertNewObjectForEntityForName:@"Entity" inManagedObjectContext:child];
[obj setValue:@"value" forKey:@"attribute"];
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"save from %@ to %@ finished", concurrencyTypeString(childConcurrencyType), concurrencyTypeString(parentConcurrencyType)]];
[self saveMOC:child completion:^(NSError *error) {
// Verify data saved all the way to the PSC
NSManagedObjectContext *localMoc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
localMoc.persistentStoreCoordinator = psc;
NSFetchRequest *fr = [NSFetchRequest fetchRequestWithEntityName:@"Entity"];
XCTAssertEqualObjects(@"value", [[[localMoc executeFetchRequest:fr error:NULL] firstObject] valueForKey:@"attribute"]);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:10 handler:nil];
}
然后,我为每个可能的父母/子女关系编写了一个测试
- (void)testThatDoingRecursiveSaveFromPrivateToPrivateWorks {
[self testWithChildConcurrencyType:NSPrivateQueueConcurrencyType
parentConcurrencyType:NSPrivateQueueConcurrencyType];
}
- (void)testThatDoingRecursiveSaveFromPrivateToMainWorks {
[self testWithChildConcurrencyType:NSPrivateQueueConcurrencyType
parentConcurrencyType:NSMainQueueConcurrencyType];
}
- (void)testThatDoingRecursiveSaveFromMainToPrivateWorks {
[self testWithChildConcurrencyType:NSMainQueueConcurrencyType
parentConcurrencyType:NSPrivateQueueConcurrencyType];
}
- (void)testThatDoingRecursiveSaveFromMainToMainWorks {
[self testWithChildConcurrencyType:NSMainQueueConcurrencyType
parentConcurrencyType:NSMainQueueConcurrencyType];
}
那么,我错过了什么
在我写这篇文章时,我想起了一个360iDev演示,演示者说您不能在NSMainQueueConcurrencyType
上下文中调用performBlock
。当时,我认为他只是说错了话,意思是禁闭,但也许社区对此有些困惑
您不能在
NSConfinementConcurrencyType
MOC上调用performBlock
,但是NSMainQueueConcurrencyType
完全支持performBlock如果您记录了错误,参数应该是e
,而不是error
。执行块是异步的,所以您能保证执行顺序吗?此外,不推荐使用限制,因此除了主线程上的主上下文之外,您应该对所有内容使用执行块。因此,我不应该在moc上的所有工作都在进行的情况下使用“dispatch_async”……我应该使用NSPrivateQueueConcurrencyType而不是限制类型。谢谢你的反馈。我肯定会尝试一种新方法。dispatch\u async
将用于其他东西。永远不要使用限制并发。始终使用performBlock
进行任何核心数据访问。如果moc,此代码将崩溃。parentContext
是主并发类型。@Mundi-为什么这么说?我又看了一遍,也许我漏掉了一个打字错误,但我看不出它应该崩溃的明显原因。嗨,是的,我想是一个打字错误。感谢您的回复:-)。