【问题标题】:CoreData asynchronous fetch causes concurrency debugger errorCoreData 异步获取导致并发调试器错误
【发布时间】:2015-10-22 02:02:55
【问题描述】:

我正在使用

-com.apple.CoreData.ConcurrencyDebug

启动时的参数以调试我的 CoreData 应用程序中的并发性。

在应用启动期间,我对主线程的托管对象上下文执行异步获取。

// set up the async request
   NSError * error = nil;
        [MOC executeRequest:asyncFetch error:&error];
        if (error) {
            NSLog(@"Unable to execute fetch request.");
            NSLog(@"%@, %@", error, error.localizedDescription);
        }

这段代码是从主线程调用的,但executeRequest: 将它排入另一个线程,我理解这是正确的行为。

并发调试器不喜欢这样,说(我认为)我在这里做错了什么。我还尝试将其包装在 [MOC performBlock:] 中,这也可以,但也会导致多线程违规。在这两种情况下,我都得到了这个:

[NSManagedObjectContext __Multithreading_Violation_AllThatIsLeftToUsIsHonor__

我是在错误地使用异步获取,还是这里的并发调试器有误?

编辑:我还尝试将它包装在 MOC performBlock 中,这应该确保它从主线程中被调用。在任何情况下,调用都是从主线程入队,但在其他地方执行。

编辑:这是获取请求:

 NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"MyEntity"];

 NSPredicate * pred = [NSPredicate predicateWithFormat:@"boolProperty == YES"];
    fetchRequest.predicate = pred;
 NSSortDescriptor * sort = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];
 fetchRequest.sortDescriptors = @[sort];      fetchRequest.propertiesToFetch = @[@"prop1", @"prop2", @"prop3", @"prop4"];

 NSPersistentStoreAsynchronousFetchResultCompletionBlock resultBlock = ^(NSAsynchronousFetchResult *result) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:kFetchCompleteNotification object:result];
        });
    };

 NSAsynchronousFetchRequest *asyncFetch = [[NSAsynchronousFetchRequest alloc]
                                              initWithFetchRequest:fetchRequest
                                              completionBlock:resultBlock];

然后我收到通知的结果:

- (void)fetchCompletedNote:(NSNotification *)note {
    NSAsynchronousFetchResult * result = note.object;
    if (![cachedResults isEqualToArray:result.finalResult]){
        cacheResults = result.finalResult;
        [self.collectionView reloadData];
    }
}

【问题讨论】:

    标签: ios multithreading core-data concurrency


    【解决方案1】:

    我认为这是 Apple 的错误。

    我提交了错误报告: https://openradar.appspot.com/30692722

    并添加了重现问题的示例项目: https://github.com/jcavar/examples/tree/master/TestAsyncFetchCoreData

    另外,如果你不想仅仅因为这个问题而禁用标志,你可能想在NSManagedObjectContext 上调换__Multithreading_Violation_AllThatIsLeftToUsIsHonor__ 方法,只是为了这部分代码。 您需要在请求执行后恢复它,这样您才能获得真正问题的违规行为。

    【讨论】:

    • 太棒了。核心数据太混乱了。我想我会在下一个项目中使用 Realm。
    • 谢谢。你有没有得到关于这个雷达的确认?即使在 iOS11 Beta2 中,它似乎仍在发生。
    • @ZS 不,没什么
    • 我正在把头发拉出来。单步执行我的代码显示我在主线程上,但并发调试显示 CoreData`+[NSManagedObjectContext Multithreading_Violation_AllThatIsLeftToUsIsHonor]:
    【解决方案2】:

    并发调试器告诉您您正在从错误的线程/队列访问MOC。您只能在上下文所属的线程/队列上调用-executeRequest: error:。如果这是NSMainQueueConcurrencyType,那么您需要在主线程上。否则,如果是NSPrivateQueueConcurrencyType,则需要使用-performBlock:-performBlockAndWait: 在正确的队列上运行。

    我添加了一个屏幕截图,见上文。请求从主线程入队,但在另一个线程上执行。

    好的,有几件事:

    1. 是断线/崩溃的线路还是您看到错误输出?
    2. 您的错误处理不正确。您应该查看-executeRequest: error: 的结果,如果结果是nil,那么您处于错误状态。即使成功,也可以填充错误变量。

    我注意到您在屏幕截图中发布的代码与您之前发布的代码不同。您是添加了-performBlock: 还是最初不包含它?

    【讨论】:

    • 当我调用 [MOC executeRequest] 时,我在主线程上。它是一个 NSMainQueueConcurrencyType moc。
    • 我最终选择了不同的路线——所以此时我无法再重新创建错误。 (即将推出应用程序!)但感谢您的帮助...实际上,我刚收到您的书,当我在这里看到您的名字时笑了:)
    • 你解决过这个问题吗?只是第一次玩这个 API,我一辈子都找不到在不抛出断言的情况下使用它的方法。它在内部调度到主队列的事实似乎表明它仅设计用于MainQueueConcurrencyType,但devstreaming.apple.com/videos/wwdc/2014/225xxgzhqylosff/225/… 表明私有队列上下文(我当前的示例)也可以使用它。我对 CoreData 有很好的经验,所以不会犯“常见”错误,例如不使用 performBlock
    • 只是一个实验,看看它们是否有用。有了这个问题,我们就不用了。
    • 这似乎是并发调试选项的错误,或者可能没有更新以考虑异步获取请求。在我自己的测试中,陷阱发生在带有 Core Data 内部方法调用堆栈的线程上。如果我删除调试标志,将按预期在主线程上调用完成块。
    猜你喜欢
    • 2015-12-09
    • 1970-01-01
    • 1970-01-01
    • 2020-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多