获取 NSManagedObject 实例的上下文是不是是线程安全的?

Posted

技术标签:

【中文标题】获取 NSManagedObject 实例的上下文是不是是线程安全的?【英文标题】:Is it thread safe to get context of NSManagedObject instance?获取 NSManagedObject 实例的上下文是否是线程安全的? 【发布时间】:2021-09-16 16:34:04 【问题描述】:

即从其他线程访问 NSManagedObject 的managedObjectContext 属性?例如:

class StoredObject: NSManagedObject 
    @NSManaged public var interestProperty: String


------- somewhere on background -------

let context = storedObject.managedObjectContext // is it safe?
context.perform  [storedObject] in
    // do something with interestProperty


---------------------------------------

【问题讨论】:

你为什么需要这个? NSManagedObjectContext 不是线程安全的,那么你会在另一个线程上用它做什么? legacy) 已知objectID 属性是线程安全的,但我不确定managedObjectContext 。无论如何,即使有 -com.apple.CoreData.ConcurrencyDebug 1 标志,也没有引发任何异常。 【参考方案1】:

NSManagedObjectContext 不是线程安全的。即使您获取此类对象的实例,在不同的线程上使用它也可能导致未定义的行为。

这是在 Apple documentation 中指定的(重点是我的):

Core Data 旨在在多线程环境中工作。然而,并不是 Core Data 框架下的每个对象都是线程安全的。要在多线程环境中使用 Core Data,请确保:

托管对象上下文在初始化时绑定到与其关联的线程(队列)。

从上下文中检索的托管对象绑定到上下文所绑定的同一个队列。

因此,虽然读取managedObjectContext 属性可能是线程安全的,因为该属性是只读的,您将无法使用它而不会冒竞争条件的风险。而且您还需要考虑托管对象的生命周期,因为除非正确保留,否则您最终可能会向已释放的托管对象询问其上下文。

【讨论】:

不错的答案。但是,由于托管对象可能处于“故障”状态,我不会对“只读”属性下太多赌注:它可能在该代理初始化后设置,何时实现.如果是这种情况,或者可能是这种情况,访问此类只读属性不是线程安全的。不知道源代码,我们不知道;) 关于“故障”状态的@CouchDeveloper 很好,这是避免使用托管上下文的另一个原因。从理论上讲,甚至不需要这样做,自然方向是托管上下文->托管对象,反之亦然。 @CouchDeveloper 错误适用于从持久存储中读取的属性。托管对象上下文不是这些属性之一,因此错误不会影响访问是否安全。 没有“解除分配”托管对象之类的东西。托管对象可能会成为故障,但这并不意味着它们会被释放。它们确实存在,您可以访问它们。 NSManagedObject.managedObjectContext 的主要问题是,如果对象与上下文解除关联,它会变成 nil。但如果你能保证它不会发生,那么抓住上下文并对其执行工作绝对没有错。 @Cristik 同意。理想情况下,应该传递对象 ID,然后在需要时构造托管对象。

以上是关于获取 NSManagedObject 实例的上下文是不是是线程安全的?的主要内容,如果未能解决你的问题,请参考以下文章

为 NSManagedObject 创建实例但不将其保存到上下文中

从尚未提交的上下文中获取数据。核心数据

从 NSManagedObject 中检索 NSManagedObjectContext

无论当前上下文状态如何,如何获取 NSManagedObject 的持久存储副本

知道 NSManagedObject 实例是新的并且尚未持久化的最简单方法是啥?

避免获取已删除的 NSManagedObject