核心数据并发`performBlockAndWait:`NSManagedObjectContext zombie

我从我发布的应用程序有一个以下的崩溃报告:

在这里输入图像说明

synchronizeMyWords方法从数据库中提取实体,与主上下文父级创build私有队列上下文,最后保存结果。 所有操作都在后台线程中。 每次应用程序进入backgroundforeground时都会调用此方法。 这是一个简化的方法:

 - (AWSTask *)synchronizeMyWords { __weak typeof(self) weakSelf = self; AWSContinuationBlock block = ^id _Nullable(AWSTask * _Nonnull task) { if ([task.result isKindOfClass:[NSArray class]]) { NSArray * records = (NSArray *)task.result; NSManagedObjectContext * context = [NSManagedObjectContext MR_contextWithParent:[NSManagedObjectContext MR_defaultContext]]; [context performBlockAndWait:^{ for (NSDictionary * info in records) { [RDRWord MR_createEntityInContext:context]; } [context save:nil]; }]; return [AWSTask taskWithResult:@YES]; } return [AWSTask taskWithError:[NSError errorWithDomain:@"" code:404 userInfo:nil]]; }; AWSExecutor * executor = [AWSExecutor defaultExecutor]; return [[self loadLocalWords] continueWithExecutor:executor withBlock:block]; } 

正如你所看到的,我正在使用魔法logging第三方库来pipe理核心数据堆栈。 这是一个创build私有队列上下文的方法:

 + (NSManagedObjectContext *) MR_contextWithParent:(NSManagedObjectContext *)parentContext { NSManagedObjectContext *context = [self MR_newPrivateQueueContext]; [context setParentContext:parentContext]; [context MR_obtainPermanentIDsBeforeSaving]; return context; } 

你可以在这里检查github上的整个NSManagedObjectContext+MagicalRecord类。

performBlockAndWait:中的context对象是如何可用的performBlockAndWait:它在释放范围之前释放? 我个人不能重现崩溃,但是我的很多用户(iOS 8.1-10设备)都受到这个问题的影响。

更新1:

这里是例如在博客上相同的报告

核心数据提供了充足的API来处理后台线程。 这些也可以通过魔法logging进入。

看起来好像你是不必要地创build了太多的线程。 我认为AWSContinuationBlockAWSExecutor的使用不是一个好主意。 可以从后台线程调用synchronizeMyWords 。 该块可能在后台线程上运行。 在块内部创build一个链接到子上下文的新的后台线程。 不清楚loadLocalWords返回什么,或者continueWithExecutor:block:如何处理线程。

数据的保存也有问题。 子上下文保存后,主上下文不保存; 大概是这种情况发生在后面,但也许与其他一些操作有关,所以你的代码之前工作的事实可能更多的是“误报”。

我的build议是简化线程代码。 您应该只限于核心数据块API。

我把@Mundi的答案标记为正确的,因为他写了你应该遵循的一般方法。 现在,我想在这里分享我如何debugging它。 首先,我了解到,可以打开xcode中的debugging并发断言。 您需要在启动时传递以下参数:

-com.apple.CoreData.ConcurrencyDebug 1

在这里输入图像说明

现在,在您的应用程序输出中,您应该看到日志消息:

2016-12-12 01:58:31.665 your-app[4267:2180376] CoreData: annotation: Core Data multi-threading assertions enabled.

一旦我打开它,我的应用程序崩溃在synchronizeMyWords方法(老实说,不仅在那里。想知道,为什么苹果默认情况下不包括在debugging模式下的并发断言?)。 我查了一下awCore库里的 defaultExecutor ,看到这个:

 + (instancetype)defaultExecutor { static AWSExecutor *defaultExecutor = NULL; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ defaultExecutor = [self executorWithBlock:^void(void(^block)()) { // We prefer to run everything possible immediately, so that there is callstack information // when debugging. However, we don't want the stack to get too deep, so if the remaining stack space // is less than 10% of the total space, we dispatch to another GCD queue. size_t totalStackSize = 0; size_t remainingStackSize = remaining_stack_size(&totalStackSize); if (remainingStackSize < (totalStackSize / 10)) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block); } else { @autoreleasepool { block(); } } }]; }); return defaultExecutor; } 

根据他们的if语句,我的continuationBlock不能保证在DISPATCH_QUEUE_PRIORITY_DEFAULT队列上执行。 于是,我创build了一个共享的dispatch_queue_t队列,并结合performBlockAndWait: CoreData方法调用了它的所有操作。 因此,现在没有崩溃,我提交了新的版本。 我会更新这个post,如果我没有得到与context僵尸崩溃报告。