【发布时间】:2014-11-22 02:06:14
【问题描述】:
我有两个NSManagedObjectContext,分别名为importContext 和childContext。 childContext是importContext的孩子,他们都是NSPrivateQueueConcurrencyType。
为了让事情远离主线程,我在importContext 的队列上做了很多工作。这项工作涉及大量获取和保存,因此将整个内容包装在 importContext 的 performBlockAndWait: 中很方便(它确实需要通过同步操作,因为我在performBlockAndWait 取决于其结果)。
在这项工作的某个时候,我可能需要从 JSON 结果创建新的托管对象。这些 JSON 值可能无效并且无法通过我的验证,因此在创建对象后,如果它们不好,我需要能够丢弃它们。这就是childContext 的用武之地。我将我的新对象插入其中,如果它的JSON 属性最终没有意义,我就放弃childContext。
当我需要保存childContext 时,问题就来了。我希望它有自己的私有队列,与其父队列分开。但是,这只会在 iOS 7(而不是 iOS 8)上导致死锁。当我在 iOS 8 模拟器和设备上运行相同的代码时,childContext 确实在单独的线程上创建了自己的队列并正确保存。
似乎当我运行 iOS 7 时,childContext 试图在父队列中执行save:,但父进程正在等待其子进程,这会导致死锁。在 iOS 8 中,这不会发生。有谁知道为什么?
这里是简化的代码:
-(NSManagedObjectContext *)importContext
{
NSManagedObjectContext* moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
moc.persistentStoreCoordinator = [self storeCoordinator];
return moc;
}
-(void)updateItems:(NSArray*)ItemDescriptions
{
[self.importContext performBlockAndWait:^{
//get info and update
...
...
if(needToCreateNewItem){
NSManagedObjectContext* childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
childContext.parentContext = self.importedContext;
//Insert and create new item
...
[childContext performBlockAndWait:^{
id newObject = [NSEntityDescription insertNewObjectForEntityForName:[self entityName]
inManagedObjectContext:childContext];
}];
...
// Do something with this object
if([newObject isReadyToSave])
__block NSError* e = nil;
__block BOOL saveSucceeded = NO;
[childContext performBlockAndWait:^{
saveSucceeded = [childContext save:&e]; // DEADLOCK ON iOS 7!!!!
}];
}
....
}
}];
}
一个简单的解决方法是将工作保留在单独的调度队列中(而不是 importContext 的队列),但我问这个问题的原因是因为我想了解发生这种情况的根本原因.我认为孩子的保存应该只发生在自己的队列中。
更新 1
回复。马库斯的问题:
updateItems:是从操作队列中的NSInvocationOperation调用的,因此它不在主队列中。-
在 iOS 7 上,我可以随时暂停应用并查看堆栈,托管对象上下文的队列将死锁:
(lldb) bt * thread #7: tid = 0xed07, 0x38546aa8 libsystem_kernel.dylib`semaphore_wait_trap + 8, queue = 'NSManagedObjectContext Queue' frame #0: 0x38546aa8 libsystem_kernel.dylib`semaphore_wait_trap + 8 frame #1: 0x385bbbac libsystem_platform.dylib`_os_semaphore_wait + 12 frame #2: 0x3848461a libdispatch.dylib`_dispatch_barrier_sync_f_slow + 138 frame #3: 0x2d4f3df2 CoreData`_perform + 102 frame #4: 0x2d4fe1ac CoreData`-[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:] + 240 frame #5: 0x2d492f42 CoreData`-[NSManagedObjectContext save:] + 826 * frame #6: 0x000c1c96 DBDevApp`__69+[DBManagedObject createWithAttributes:inManagedObjectContext:error:]_block_invoke77(.block_descriptor=<unavailable>) + 118 at DBManagedObject.m:117 frame #7: 0x2d4f6934 CoreData`developerSubmittedBlockToNSManagedObjectContextPerform + 88 frame #8: 0x3847e81e libdispatch.dylib`_dispatch_client_callout + 22 frame #9: 0x384847ca libdispatch.dylib`_dispatch_barrier_sync_f_invoke + 26 frame #10: 0x2d4f6a72 CoreData`-[NSManagedObjectContext performBlockAndWait:] + 106 frame #11: 0x000c1916 DBDevApp`+[DBManagedObject createWithAttributes:inManagedObjectContext:error:](self=0x005c1790, _cmd=0x0054a033, attributes=0x188e context=0x17500800, error=0x02e68ae8) + 658 at DBManagedObject.m:116 frame #12: 0x000fe138 DBDevApp`-[DBAPIController createOrUpdateItems:withIDs:IDKeys:ofClass:amongExistingItems:withFindByIDPredicate:](self=0x17775de0, _cmd=0x0054de newItemDescriptions=0x188eada0, itemIDs=0x18849580, idKey=0x0058e290, class=0x005c1790, existingItems=0x1756b560, findByID=0x18849c80) + 2472 at DBAPIController.m:972 frame #13: 0x00100ca0 DBDevApp`__39-[DBAPIController updatePatientGroups:]_block_invoke(.block_descriptor=0x02e68ce0) + 476 at DBAPIController.m:1198 frame #14: 0x2d4f6934 CoreData`developerSubmittedBlockToNSManagedObjectContextPerform frame #15: 0x3847e81e libdispatch.dylib`_dispatch_client_callout + 22 frame #16: 0x384847ca libdispatch.dylib`_dispatch_barrier_sync_f_invoke + 26 frame #17: 0x2d4f6a72 CoreData`-[NSManagedObjectContext performBlockAndWait:] + 106 frame #18: 0x00100a96 DBDevApp`-[DBAPIController updatePatientGroups:](self=0x17775de0, _cmd=0x0054dfcd, groupsArray=0x188eada0) + 214 at DBAPIController.m:1191 frame #19: 0x2d721584 CoreFoundation`__invoking___ + 68 frame #20: 0x2d66c0da CoreFoundation`-[NSInvocation invoke] + 282 frame #21: 0x2e0f3d2c Foundation`-[NSInvocationOperation main] + 112 frame #22: 0x2e0515aa Foundation`-[__NSOperationInternal _start:] + 770 frame #23: 0x2e0f576c Foundation`__NSOQSchedule_f + 60 frame #24: 0x38484f10 libdispatch.dylib`_dispatch_queue_drain$VARIANT$mp + 488 frame #25: 0x38484c96 libdispatch.dylib`_dispatch_queue_invoke$VARIANT$mp + 42 frame #26: 0x38485a44 libdispatch.dylib`_dispatch_root_queue_drain + 76 frame #27: 0x38485d28 libdispatch.dylib`_dispatch_worker_thread2 + 56 frame #28: 0x385c0bd2 libsystem_pthread.dylib`_pthread_wqthread + 298
我上面显示的代码是一个简化版本。我创建新子上下文的部分位于名为DBManagedObject 的类中。这是整个堆栈的屏幕截图:
更新 2 - 解释 DBManagedObject
DBManagedObject 是我所有核心数据类的基类。它基本上处理与 JSON 解析字典的转换。它有3个主要方法:+createWithAttributes:inManagedObjectContext:error:、-updateWithAttributes:error:和attributes。
-
+createWithAttributes:inManagedObjectContext:error::创建提供的托管对象上下文的子上下文,在子上下文中插入一个新对象并在该对象上调用updateWithAttributes:error:。如果更新成功(即,我们要在此对象上设置的所有值都有意义),它会保存子上下文,获取对 MOC 中作为参数传入的新对象的引用,并返回该引用: /p>NSManagedObjectContext* childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; childContext.parentContext = context; __block id newObject; [childContext performBlockAndWait:^{ newObject = [NSEntityDescription insertNewObjectForEntityForName:[self entityName] inManagedObjectContext:childContext]; }]; if ([newObject updateWithAttributes:attributes error:error]) { NSError* e = nil; if ([childContext save:&e]) { id parentContextObject = [context objectWithID:[(NSManagedObject*)newObject objectID]]; return parentContextObject; } else { if (error != NULL) { *error = e; } return nil; } } else return nil; updateWithAttributes:error::将 JSON 键之间的键转换为我在数据模型中用作实体属性的键是否繁重。 (即'first_name'变成'firstName')。如果需要,它还会格式化 JSON 值(日期字符串变为NSDates)。它还设置关系。
【问题讨论】:
-
在将新对象插入子上下文的部分中,您似乎缺少 performBlock 或 performBlockAndWait,您可以使用该部分代码更新您的问题吗?
-
这是我简化代码中的一个问题。更新它以反映它在代码中的真实情况。
标签: ios core-data concurrency nsmanagedobjectcontext