CoreData 和一对多关系的并发错误

Posted

技术标签:

【中文标题】CoreData 和一对多关系的并发错误【英文标题】:Concurrency error with CoreData and one-to-many relationship 【发布时间】:2012-03-28 12:41:11 【问题描述】:

我目前正在开发一个使用 CoreData 作为数据持久层的 iPhone 应用程序。在其中一种情况下,我需要在后台进行一些处理和批量更新托管对象,以免阻塞 UI 线程。

在Apple's recommendation 之后,我有 2 个不同的托管对象上下文(一个用于主线程,一个用于后台线程)。这是分配代码(在我的应用程序委托中):

// Object context for Main Thread
_managedObjectContext = [[NSManagedObjectContext alloc] init];
_managedObjectContext.persistentStoreCoordinator = _coordinator;

// Object context for the background thread
dispatch_group_t myGroup = dispatch_group_create();
dispatch_group_async(
    myGroup,
    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),
    ^
        _bgManagedObjectContext = [[NSManagedObjectContext alloc] init];
        _bgManagedObjectContext.persistentStoreCoordinator = _coordinator;
    
);

dispatch_group_wait(myGroup, DISPATCH_TIME_FOREVER);
dispatch_release(myGroup);

应用启动时,主线程仅获取对象进行显示,而后台线程从网络获取新数据并更新CoreData。

我使用 AFNetworking 并确保所有回调都在后台线程上执行。片段:

NSURLRequest *request = [NSURLRequest requestWithURL:webserviceURL];
AFHTTPRequestOperation *operation =
        [[[AFHTTPRequestOperation alloc] initWithRequest:request] autorelease];

[operation setCompletionBlockWithSuccess:
    ^(AFHTTPRequestOperation *operation, id responseObject) 
        // Getting the managed object context created on the bg thread
        NSManagedObjectContext *context = [self bgManagedObjectContext];

        //
        // Snip...
        // Fetch objects, update them
        //

     failure:nil];

operation.successCallbackQueue =
        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
[operation start];

在回调代码中,我根据属性获取新对象,然后更新它们。一切都很好,直到我开始更新相关对象......

我的主要实体 (Place) 定义了与 Keyword(穷人的全文搜索...)的单边 一对多 关系,并且当我重新索引Place,我首先删除所有关联的Keywords。

// `self.searchWords` is a @dynamic property
for (NSManagedObject *word in self.searchWords) 
    // context is still the background thread's object context
    // and we're still on the background thread
    [context deleteObject:word];

[self removeSearchWords:oldSearchWords];

到达[context deleteObject:word] 时,我遇到了异常:NSManagedObjectContext 无法删除其他上下文中的对象

当我调试和检查变量时,我可以看到:

self._cd_managedObjectContext 是后台线程的对象上下文 word._cd_managedObjectContext 是主线程的对象上下文

我觉得这完全令人困惑,我不明白为什么获取的关系最终会与不同的对象上下文相关联。

我最终可以在后台上下文中重新获取关联的Keyword,然后从这里删除它,但在这种情况下它是唯一的解决方案吗?还是我做错了什么?

【问题讨论】:

【参考方案1】:

克莱门特,

我建议两件事。首先,必须在执行它们的线程上创建后台 MOC。使用 GCD,您不能永久声明任何线程。因此,这引出了我的第二点。不要坚持你的背景 MOC。它们的创建成本很低。我怀疑您的大部分问题是由于没有使 MOC 保持同步。持久性存储确实是实现这一点的机制。

安德鲁

【讨论】:

感谢您的建议,我实际上最终在需要时按需创建了后台 MOC。但是,我在问题中描述的问题仍然存在。 Clément,那么您遇到了数据库一致性问题。这对于许多多线程数据库应用程序很常见。 IOW,您没有 CD 问题。就我而言,我通过在使用对象很久之后删除对象来解决这个问题。您也可以通过从不保存瞬态对象来解决此问题。在 CD 中,这最好在单独的 MOC 中完成。然后,在完成瞬态对象后删除 MOC。安德鲁

以上是关于CoreData 和一对多关系的并发错误的主要内容,如果未能解决你的问题,请参考以下文章

CoreData 获取所有一对多关系

在一对多 CoreData 关系错误上调用“计数”是不是会将集合中的所有对象都带入内存?

CoreData 在一对多关系中编辑一个属性

一对多关系CoreData Swift

一对多关系的CoreData谓词

CoreData 一对多关系