首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >核心数据并发性` NSManagedObjectContext:`NSManagedObjectContext僵尸

核心数据并发性` NSManagedObjectContext:`NSManagedObjectContext僵尸
EN

Stack Overflow用户
提问于 2016-12-10 07:39:21
回答 2查看 514关注 0票数 0

下面是我发布的应用程序的崩溃报告:

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

代码语言:javascript
复制
- (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];
}

正如您所看到的,我正在使用魔法记录第三方库来管理核心数据堆栈。下面是一种创建私有队列上下文的方法:

代码语言:javascript
复制
+ (NSManagedObjectContext *) MR_contextWithParent:(NSManagedObjectContext *)parentContext
{
    NSManagedObjectContext *context = [self MR_newPrivateQueueContext];
    [context setParentContext:parentContext];
    [context MR_obtainPermanentIDsBeforeSaving];
    return context;
}

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

context对象逃离作用域之前,performBlockAndWait:内部发布的对象是如何可用的?我个人无法重现这次崩溃,但我的许多用户(iOS 8.1 - 10设备)都受到这个问题的影响。

更新1:

例如,下面是关于博客的同一个报告

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-12-10 09:31:09

核心数据提供了足够的API来处理后台线程。这些也可以通过魔法记录获得。

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

数据的保存也有问题。在保存子上下文之后不会保存主上下文;这可能会在稍后发生,但可能与其他操作有关,因此您的代码以前工作过的事实可能更像是“假阳性”。

我的建议是简化线程代码。您应该将自己限制在核心数据块API上。

票数 1
EN

Stack Overflow用户

发布于 2016-12-12 06:02:22

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

-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方法中崩溃(老实说,不仅仅是在那里。不知道,为什么Apple在调试模式中默认不包括并发断言?)。我查看了defaultExecutorAWSCore库中的内容,并看到了以下内容:

代码语言:javascript
复制
+ (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队列上执行。因此,我创建了一个共享的dispatch_queue_t队列,并结合performBlockAndWait: CoreData方法调用该队列上的所有操作。因此,现在没有崩溃,我提交了新的版本。如果我没有收到任何context僵尸的崩溃报告,我会更新这篇文章。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41073296

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档