【问题标题】:Unacceptable type of value for to-one relationship: property = "user"; desired type = User; given type = User;一对一关系不可接受的值类型:property = "user";所需类型 = 用户;给定类型=用户;
【发布时间】:2017-03-28 15:15:11
【问题描述】:

我遇到了核心数据的有线问题。使用 iOS 10 中的 Swift3,我每次获取或存储数据时都会获取托管对象上下文

 func getContext () -> NSManagedObjectContext {
    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    return appDelegate.persistentContainer.viewContext
}

在我的应用中,我有两个实体“用户”和“分类帐”。我想为一个用户分配一个分类帐,但一个用户可以有多个分类帐。因此,我有一个可以显示用户的 UserTableView 和一个创建用户的 UserViewController 类。我对账本也是如此。在创建分类帐时,我还会获得所有用户的列表,我从中选择一个并且应该分配给分类帐,反之亦然。

当像前面提到的那样保存时,我得到了错误

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for to-one relationship: property = "user"; desired type = User; given type = User;

我的数据模型如下所示: Data Model

非常感谢任何帮助:)

【问题讨论】:

  • 你解决了吗?

标签: ios swift core-data nsmanagedobject nsmanagedobjectcontext


【解决方案1】:

我遇到了同样的问题。我认为@Chris 已经清楚地给出了原因。进行测试时,内存中有多个实例。

解决方案是将NSPersistentContainer init 部分移动到setUpWithError 和tearDownWithError。

在下面的示例中,CoreDataManager 负责处理 NSPersistentContainer 的 init。

代码如下:

之前

class FooTests: XCTestCase {
    var manager: CoreDataManager = CoreDataManager()
}

之后

 class FooTests: XCTestCase {

        var manager: CoreDataManager?

        override func setUpWithError() throws {
             manager = CoreDataManager(inMemory: true)
        }

        override func tearDownWithError() throws {
             manager = nil
        }

    }

【讨论】:

    【解决方案2】:

    我有同样的问题,但我很确定我没有像之前的答案那样有 2 个核心数据堆栈。

    最后我意识到我已经使用lazy 关键字初始化了对NSStoreCoordinator 的引用以及视图上下文和背景上下文。我还有一堆代码在后台线程中做一些繁重的工作并使用所述堆栈进行保存。

    Apple 的文档说:“如果用惰性修饰符标记的属性是 被多个线程同时访问并且属性没有 尚未初始化,无法保证该属性将 只初始化一次。”

    因此,这种不幸的情况是解决此错误的另一种方法。解决方案:确保在您的核心数据堆栈中不要lazy 任何东西,特别是在执行大量后台/前台线程工作时。

    我希望它对将来的某人有所帮助。祝你未来的开发者,如果你在这里:祝你好运。

    【讨论】:

      【解决方案3】:

      我遇到了同样的问题。就我而言,它发生在我运行单元测试时。在这种情况下,内存中同时存在两个核心数据堆栈,一个用于应用程序工具,另一个用于我正在运行的单元测试。

      解决这个问题的第一个线索是在设置关系属性之前放置一个断言,以确保我设置的对象的实体类型与关系的预期实体类型相同。它们应该是相同的,而在我的情况下它们不是。

      在我的例子中,我有一个 MatchRequest 与 Player 一对一的关系,称为“发起者”。所以我的断言如下所示:

          let player = try Player.findLocal(for: matchRequest.initiator, in: moc, createIfMissing: true)
          let expectedEntity = self.entity.relationshipsByName["initiator"]!.destinationEntity!
          assert(player!.entity === expectedEntity, "Player returned doesn't have the same entity type")
          self.initiator = player
      

      上述断言失败,我怀疑这与 Core Data 使用的导致参数异常的断言相似。

      检查 Player.entity() 时,它会返回导致失败的同一实体实例。

      我认为问题的根源在于 Core Data 正在为实体设置一些静态属性,这些实体将在核心数据堆栈之间错误地共享。从一个堆栈而不是另一个堆栈调用时,调用 MyManagedObject.entity() 将正常工作。

      因此,为了解决这个问题,当我创建我的 Player 对象以放入关系中时,我使用旧的 NSEntityDescription.insertNewObject(...) API 获取实体,而不是新的 MyManagedObject(context:) 构造函数。这可确保为给定的托管对象上下文使用正确的实体。

      所以,回顾一下:

      // SOMETIMES FAILS if you have more than one core data stack:
      result = Player(context: managedObjectContext)
      
      // ALWAYS WORKS:
      result = NSEntityDescription.insertNewObject(forEntityName: "Player", into: managedObjectContext) as? Player
      

      【讨论】:

      • 这解决了我在基于文档的应用程序中使用多个对象上下文时的问题。每个文档都有自己的上下文,由 NSPersistantDocument 创建,但是在创建对象时我会遇到这个确切的崩溃。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-31
      • 2020-07-13
      • 2021-10-19
      • 2021-10-28
      • 1970-01-01
      • 2019-06-19
      • 2016-09-01
      相关资源
      最近更新 更多