我可以在并发 GCD 队列中使用 NSManagedObjectContext 和 NSPrivateQueueConcurrencyType

Posted

技术标签:

【中文标题】我可以在并发 GCD 队列中使用 NSManagedObjectContext 和 NSPrivateQueueConcurrencyType【英文标题】:Can I use NSManagedObjectContext with NSPrivateQueueConcurrencyType in a concurrent GCD queue 【发布时间】:2013-10-24 07:47:49 【问题描述】:

到目前为止,我的方法是这样的:

1- 像这样初始化的主上下文:

_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.model];
if(![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) 
    DDLogModel(@"Unresolved error %@", error.localizedDescription);
    return;


self.context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
self.context.persistentStoreCoordinator =_persistentStoreCoordinator;

2- 然后,当我着手创建核心数据对象或同时修改它们的关系时:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
    NSManagedObjectContext *tempContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
tempContext.persistentStoreCoordinator = self.persistentStoreCoordinator;

//  Do stuff

[tempContext save:nil];
);

3- 最后,主上下文通过NSManagedObjectContextDidSaveNotification进行合并

但是,我最近看到了一种不同的方法,在第 2 步中,他们改为使用 NSPrivateQueueConcurrencyType 创建上下文,使其成为主上下文的子上下文,并通过 -performBlock: 进行任何工作

默认情况下最后一种方法是并发的吗(即使没有明确地调度它)?或者比我解释的方法有什么优势?

让我失望的另一件事是,即使上下文具有 parentContextpersistentStoreCoordinator 属性,设置后者似乎意味着无法设置前者。也就是说,具有持久存储协调器的上下文实际上将该存储协调器作为其父上下文?

更新: 另一个有趣的事情是,使用我上面描述的方法(使用 GCD),当我做[tempContext save:] 时,我会时不时地得到一个奇怪的行为:没有返回错误(假设我确实传入了一个 NSError 对象,与示例不同) ,但是如果我将通用的 Objective-C 异常指针设置为 on,后台线程确实会停在那里,就好像有异常一样。但是,如果我继续,应用程序不会崩溃并继续运行,主 moc 似乎还不错。

【问题讨论】:

【参考方案1】:

你是对的。 performBlock 将自动在后台执行工作。在某些情况下,使用当前线程(主线程或后台线程)可能有意义,在这种情况下,您可以使用performBlockAndWait。使用子上下文是推荐的方法。

我想您的设置也可以正常工作。我想使用子上下文的优势在于更结构化的保存方法,即“向上推”保存到父上下文中。只有父上下文才会真正接触持久存储,所以这在线程安全方面更好。

您的最后一个问题不清楚。一个上下文只能有一个上下文作为其父上下文,而不是一个持久存储协调器。但是,您可能会感到困惑的是,在 ios 5 之前只有一个“父存储”,并且随着子上下文的引入,它可以替换上下文时间>。阅读所有相关信息here。

【讨论】:

当您说performBlock 确实在后台工作时,您是否暗示“同时”?因为我把它当作“后台线程,可能只与主线程一起被抢占”。也就是说,时间切片但仍然是连续的。如果是这样,我认为我的方法会更快,因为我明确地同时进行。另外,我的最后一个问题是因为我观察到,如果我创建一个上下文并为其分配一个持久存储坐标。然后,如果我尝试为其分配一个父上下文,它会告诉我它已经有一个(但我只为它分配了一个存储坐标!)。 是的,我的意思是同时。应该使用NSPrivateQueueConcurrencyType 初始化子上下文以使其正常工作。效果应该是一样的。除非您有多个处理器,否则并发始终适用于时间片。 -- 对于 PSC 问题,请阅读我的答案! 我还有几个问题:带有 NSPrivateQueueConcurrencyType 的 moc 是否隐含地创建自己的队列?或者我还需要在 GCD 并发队列中创建它吗?此外,如果一个孩子将更改推送到父级并且只有父级保存,这是否意味着如果我告诉一个正在处理单个对象的子 moc 进行保存,则父级最终会保存,因此任何其他有待处理的对象父 moc 中的更改也会保存吗?因为我只想保存子 moc 中的更改 另一个有趣的事情是,使用我上面描述的方法(使用 GCD),当我做[tempContext save:] 时,我会时不时地得到一个奇怪的行为:没有返回错误(假设我确实传递了一个NSError 对象,与示例不同),但如果我将通用的 Objective-C 异常指针设置为 on,后台线程确实会停在那里,就好像有异常一样。但是,如果我继续,应用程序不会崩溃并继续运行,主 moc 似乎还不错。 实际上,我一直在测试[[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType],它从不在主线程之外的线程中执行。所以它甚至不是并发的:/

以上是关于我可以在并发 GCD 队列中使用 NSManagedObjectContext 和 NSPrivateQueueConcurrencyType的主要内容,如果未能解决你的问题,请参考以下文章

iOS 多线程GCD的基本使用

GCD的使用

GCD 全局并发队列并不总是并发(iOS 设备)?

GCD编程-串行队列与并发队列

Object-C关于GCD多线程的使用

将调度障碍 (GCD) 添加到自定义队列但不添加到全局并发队列