在iOS 7上具有专用队列死锁父级的子上下文上执行PerformLockandWait
我有两个名为在iOS 7上具有专用队列死锁父级的子上下文上执行PerformLockandWait,ios,core-data,concurrency,nsmanagedobjectcontext,Ios,Core Data,Concurrency,Nsmanagedobjectcontext,我有两个名为importContext和childContext的NSManagedObjectContextschildContext是importContext的子级,它们都是NSPrivateQueueConcurrencyType 为了让事情远离主线程,我在importContext的队列上做了大量工作。这项工作涉及大量的获取和保存,因此可以方便地将整个内容封装在importContext的performBlockAndWait:中(它确实需要同步操作,因为在performBlockAn
importContext
和childContext
的NSManagedObjectContext
schildContext
是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
Re。马库斯的问题:
updateItems:
是从操作队列中的NSInvocationOperation
调用的,因此它脱离了主队列(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
updateWithAttributes:error:
:将JSON键之间的键转换为我在数据模型中用作实体属性的键的繁重工作。(即“名字”变为“名字”)。如果需要,它还会格式化JSON值(日期字符串变成NSDate
s)。它还可以建立关系谁在呼叫
-updateItems:
?如果在主队列中出现这种情况,您就有一个问题,因为您正在阻止它
假设情况并非如此,您能否从显示截止日期的Xcode共享线程堆栈?特别是扩展队列和扩展主队列
我将在仔细查看该堆栈后更新我的答案
Quellish是正确的,您没有正确地插入子对象。该子MOC上的任何活动都应位于-performBlock:
或-performBlock和wait:
的内部。我将扩展-performBlockAndWait:
以覆盖对象的整个创建和决策,而不仅仅是保存
更新1
-createWithAttributes:inManagedObjectContext:error:
做什么?这种方法似乎做了一些不合适的事情。也许是想强迫永久身份证什么的
更新2
正如所怀疑的那样,您的-createWithAttributes:inManagedObjectContext:error:
是您的问题。当您调用-objectWithID:
时,会导致在NSPersistentStoreCoordinator
上触发一个fetch,从而导致锁定
此外,此方法没有任何帮助。仅仅为了创建一个对象然后立即在另一个上下文中抓取该对象而创建上下文是绝对没有价值的。弊无利。完全删除它,只需在实际要使用它的上下文中创建对象。将其从正在使用的上下文中保存或丢弃
不要太聪明。通过查看您的代码,我发现您有两个嵌套的[childContext performBlockAndWait:^{。删除其中一个应该可以在ios7中解决您的问题。代码已经在该线程中运行,您不必再这样做 始终检查同一上下文中是否有嵌套的PerformBlock。这导致我的应用程序在ios7中出现死锁,并在ios8中工作
检查的方法是,当您看到死锁时,按“调试器中的暂停”并查看所有线程正在运行的块。查看特定代码并检查嵌套块。您似乎缺少一个performBlock或performBlock,请在将新对象插入子上下文的部分等待,是否可以更新您的问题w这是我的简化代码中的一个问题。更新了它以反映它在代码中的真实情况。更新了我的问题并添加了堆栈跟踪。我巧妙地解决了另一个问题,即插入一个对象,在其上设置一些关系,然后决定删除它,这会导致核心数据以“悬空引用错误”。这是另一个线程的主题,但至少,由于你的评论,我理解了死锁发生的原因。关于为什么在iOS 8上可以这样做,有什么想法吗?可能是性能改进。即使在iOS 8中可以这样做,仍然有很多代码没有价值。
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;