核心数据并发问题以及如何解决

时间:2017-08-30 23:11:29

标签: ios objective-c multithreading core-data

我在Core Data应用中使用了多个上下文,并且最近有一些核心数据并发崩溃。我已添加-com.apple.CoreData.ConcurrencyDebug 1以帮助跟踪这些问题,但我不了解如何解决显示的问题。

这是我正在做的事情:

- (void)getEvents:(void (^)(NSArray *fetchedItems))completionBlock {

    // Initialize Fetch Request
    NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"ZSSCDEvent"];

    // Initialize Asynchronous Fetch Request
    NSAsynchronousFetchRequest *asynchronousFetchRequest = [[NSAsynchronousFetchRequest alloc] initWithFetchRequest:request completionBlock:^(NSAsynchronousFetchResult *result) {

        dispatch_async(dispatch_get_main_queue(), ^{

            // Dismiss Progress HUD
            [SVProgressHUD dismiss];

            // Process Asynchronous Fetch Result
            if (result.finalResult) {

                NSArray *results = result.finalResult;

                completionBlock(results);

                // Reload Table View
                [self.activityIndicator stopAnimating];
                [self.tableViewList reloadData];

            }

        });
    }];

    // Execute Asynchronous Fetch Request
    [self.managedObjectContext performBlock:^{

        // Execute Asynchronous Fetch Request
        NSError *asynchronousFetchRequestError = nil;
        NSAsynchronousFetchResult *asynchronousFetchResult = (NSAsynchronousFetchResult *)[self.managedObjectContext executeRequest:asynchronousFetchRequest error:&asynchronousFetchRequestError];

        if (asynchronousFetchRequestError) {
            NSLog(@"Unable to execute asynchronous fetch result.");
            NSLog(@"%@, %@", asynchronousFetchRequestError, asynchronousFetchRequestError.localizedDescription);
        }

    }];

}

这给了我一个Enqueued from com.apple.main-thread (Thread 1)错误。这是我感到困惑的地方,因为我在主线程上运行它并且不认为我需要在这里使用我的私有上下文。

enter image description here

关于我为什么会遇到并发问题的想法?

编辑:看起来其他人有确切的问题,并认为这是一个Xcode错误:CoreData asynchronous fetch causes concurrency debugger error

1 个答案:

答案 0 :(得分:0)

每个managedObject都有一个上下文。每个上下文都有一个且只能运行一个线程。 ManagedObjects不是线程安全的 - 甚至不用于读取。使用完成块传递managedObjects是一种不好的做法。可能很难弄清楚哪个托管对象应该在哪个线程上。而且,即使你在正确的线程上传递它,它仍然是一个不好的做法。当您执行dispatch_async时,实体可能已在过渡期间从数据库中删除,并且访问managedObject将导致崩溃。一个好的做法是,任何执行提取的方法都应该明确告知要使用哪个上下文并同步返回。在您的代码中,该方法使用self.managedObjectContext,但我无法知道查看代码与哪个线程相关。此外,指向上下文的指针可能会发生变化,这可能会导致很难追踪的错误。

NSAsynchronousFetchResult包含managedObjects,因此不是线程安全的,只能在完成块内部使用(在对象的正确线程上运行)。你不能将它们传递给另一个线程。如果你在块中返回它们,那么该代码也不能将它们传递给另一个线程。你应该在块内做任何你需要做的事情然后丢弃它们。

如果需要向用户显示信息,那么通常最好同步对主线程进行提取并获取与主线程上下文关联的managedObjects。如果您的提取时间很长,那么您应该解决这个问题 - 没有理由让提取花费这么长时间。在您的情况下,您似乎正在获取数据库中的所有项目。那是一个错误。您应该使用谓词来获取所需的谓词。

另一种可能性是将managedObjects的值复制到线程安全对象(字典或NSObject子类)中。

TL; DR 您可能不需要NSAsynchronousFetchRequest。在主线程上使用常规获取请求并同步返回。使用谓词将结果仅限制为实际显示的对象。