[核心数据]:多个 NSEntityDescriptions 声称 NSManagedObject 子类 'Core Data Model' 所以 +entity 无法消除歧义

[Core Data]:Multiple NSEntityDescriptions claim the NSManagedObject subclass 'Core Data Model' so +entity is unable to disambiguate

将数据保存到 CoreData 时出现以下错误。我使这个项目变得非常简单,以便您查看我的项目。项目中有Local json。您可以下载并测试我的项目。

GitLab 项目 Link: Core Data Test Project

2021-08-20 20:17:03.576453+0300 CoreDataTest[27924:2275095] [error] warning: Multiple NSEntityDescriptions claim the NSManagedObject subclass 'QuestionCD' so +entity is unable to disambiguate.
CoreData: warning: Multiple NSEntityDescriptions claim the NSManagedObject subclass 'QuestionCD' so +entity is unable to disambiguate.
2021-08-20 20:17:03.576623+0300 CoreDataTest[27924:2275095] [error] warning:     'QuestionCD' (0x28090c9a0) from NSManagedObjectModel (0x281d3d400) claims 'QuestionCD'.
CoreData: warning:       'QuestionCD' (0x28090c9a0) from NSManagedObjectModel (0x281d3d400) claims 'QuestionCD'.
2021-08-20 20:17:03.576675+0300 CoreDataTest[27924:2275095] [error] warning:     'QuestionCD' (0x2809142c0) from NSManagedObjectModel (0x281d33ca0) claims 'QuestionCD'.
CoreData: warning:       'QuestionCD' (0x2809142c0) from NSManagedObjectModel (0x281d33ca0) claims 'QuestionCD'.
2021-08-20 20:17:03.576720+0300 CoreDataTest[27924:2275095] [error] warning:     'QuestionCD' (0x2809148f0) from NSManagedObjectModel (0x281d4cf50) claims 'QuestionCD'.
CoreData: warning:       'QuestionCD' (0x2809148f0) from NSManagedObjectModel (0x281d4cf50) claims 'QuestionCD'.
2021-08-20 20:17:03.576762+0300 CoreDataTest[27924:2275095] [error] warning:     'QuestionCD' (0x280914fd0) from NSManagedObjectModel (0x281d4e490) claims 'QuestionCD'.
CoreData: warning:       'QuestionCD' (0x280914fd0) from NSManagedObjectModel (0x281d4e490) claims 'QuestionCD'.
2021-08-20 20:17:03.576804+0300 CoreDataTest[27924:2275095] [error] error: +[QuestionCD entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass
CoreData: error: +[QuestionCD entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass
2021-08-20 20:17:03.585701+0300 CoreDataTest[27924:2275095] [general] 'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release
2021-08-20 20:17:03.585897+0300 CoreDataTest[27924:2275095] -[CoreDataTest.QuestionList encodeWithCoder:]: unrecognized selector sent to instance 0x281028400
2021-08-20 20:17:03.586179+0300 CoreDataTest[27924:2275095] *** -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it.
2021-08-20 20:17:03.586297+0300 CoreDataTest[27924:2275095] [error] error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x280b0d5c0> , <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo of (null)
CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x280b0d5c0> , <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo of (null)
2021-08-20 20:17:03.586383+0300 CoreDataTest[27924:2275095] [error] error: -executeRequest: encountered exception = <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo = (null)
CoreData: error: -executeRequest: encountered exception = <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo = (null)
2021-08-20 20:17:03.586553+0300 CoreDataTest[27924:2275095] [general] 'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release
2021-08-20 20:17:03.586649+0300 CoreDataTest[27924:2275095] -[CoreDataTest.QuestionList encodeWithCoder:]: unrecognized selector sent to instance 0x281028400
2021-08-20 20:17:03.586799+0300 CoreDataTest[27924:2275095] *** -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it.
2021-08-20 20:17:03.586874+0300 CoreDataTest[27924:2275095] [error] error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x280b0d5c0> , <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo of (null)
CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x280b0d5c0> , <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo of (null)
2021-08-20 20:17:03.586942+0300 CoreDataTest[27924:2275095] [error] error: -executeRequest: encountered exception = <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo = (null)
CoreData: error: -executeRequest: encountered exception = <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo = (null)
2021-08-20 20:17:03.587088+0300 CoreDataTest[27924:2275095] [general] 'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release
2021-08-20 20:17:03.587163+0300 CoreDataTest[27924:2275095] -[CoreDataTest.QuestionList encodeWithCoder:]: unrecognized selector sent to instance 0x281028400
2021-08-20 20:17:03.587301+0300 CoreDataTest[27924:2275095] *** -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it.
2021-08-20 20:17:03.587372+0300 CoreDataTest[27924:2275095] [error] error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x280b0d5c0> , <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo of (null)
CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x280b0d5c0> , <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo of (null)
2021-08-20 20:17:03.587434+0300 CoreDataTest[27924:2275095] [error] error: -executeRequest: encountered exception = <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo = (null)
CoreData: error: -executeRequest: encountered exception = <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo = (null)
2021-08-20 20:17:03.599299+0300 CoreDataTest[27924:2275095] [general] 'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release
2021-08-20 20:17:03.599618+0300 CoreDataTest[27924:2275095] -[CoreDataTest.QuestionList encodeWithCoder:]: unrecognized selector sent to instance 0x281028400
2021-08-20 20:17:03.599908+0300 CoreDataTest[27924:2275095] *** -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it.
2021-08-20 20:17:03.600018+0300 CoreDataTest[27924:2275095] [error] error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x280b04000> , <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo of (null)
CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x280b04000> , <shared NSKeyedUnarchiveFromData transformer> threw while encoding a value. with userInfo of (null)
Failed to save selected category: A Core Data error occurred.

源代码编辑器功能受限错误(这是什么?/为什么?):

核心数据实体:

核心数据保存功能:

func saveSelectedQuestion(questionTitle: String, id: String, questions: [QuestionList]) {
    
    let questionsCD = QuestionCD(context: persistentContainer.viewContext)
    questionsCD.title = questionTitle
    questionsCD.id = id
    questionsCD.questions = questions
    
    do {
        try persistentContainer.viewContext.save()
    } catch {
        print("Failed to save selected category: \(error.localizedDescription)")
    }
}

型号:

class QuestionContainer: NSObject, Codable {
    
    var questionCategories: [Question]
    
    init(questionCategories: [Question]) {
        self.questionCategories = questionCategories
    }
}

class Question: NSObject, Codable {
    
    var title: String
    var id: String
    var questions: [QuestionList]
    
    init(title: String, id: String, questions: [QuestionList]) {
        self.title = title
        self.id = id
        self.questions = questions
    }
}

public class QuestionList: NSObject, Codable {
    
    init(id: String, question: String, isQuestionImage: Bool, isSectionImage: Bool, imageURL: String, imageData: Data? = nil, sections: Sections, selected: String, correct: String) {
        self.id = id
        self.question = question
        self.isQuestionImage = isQuestionImage
        self.isSectionImage = isSectionImage
        self.imageURL = imageURL
        self.imageData = imageData
        self.sections = sections
        self.selected = selected
        self.correct = correct
    }
    
    var id: String
    var question: String
    var isQuestionImage, isSectionImage: Bool
    var imageURL: String
    var imageData: Data?
    var sections: Sections
    var selected: String
    var correct: String
}

public class Sections: NSObject, Codable {
    init(a: String, b: String, c: String, d: String) {
        self.a = a
        self.b = b
        self.c = c
        self.d = d
    }
    
    var a, b, c, d: String
    private enum CodingKeys: String, CodingKey {
       case a = "A"
       case b = "B"
       case c = "C"
       case d = "D"
    }
}

我保存数据的页面:

我正在保存此页面的数据。

struct QuestionCategoryCellView: View {
    @ObservedObject var questionCategoryCellViewModel = QuestionCategoryCellViewModel()
    var questionTitle: String
    var questionID: String
    @Binding var questions: [QuestionList]
    var body: some View {
        Button(action: {
            print("title: \(questionTitle)")
            if questionCategoryCellViewModel.searchCategoryInCoreData(id: questionID) {
                print("already saved")
            } else {
                questionCategoryCellViewModel.saveSelectedQuestionsToCoreData(questionTitle: questionTitle, id: questionID, questions: questions)
            }
        }) {
            Text(questionTitle)
        }
    }
}

在您的错误消息中有四个这样的消息对:

2021-08-20 20:17:03.576623+0300 CoreDataTest[27924:2275095] [error] warning:     'QuestionCD' (0x28090c9a0) from NSManagedObjectModel (0x281d3d400) claims 'QuestionCD'.
CoreData: warning:       'QuestionCD' (0x28090c9a0) from NSManagedObjectModel (0x281d3d400) claims 'QuestionCD'.

但是仔细一看,模型的内存地址每次都不一样。这里是0x281d3d400,其他三次都不一样。那是因为内存中有四个不同的托管对象模型副本。您不止一次加载模型并混淆了核心数据。它是同一个实体,但 Core Data 对在哪里使用它感到困惑。

查看您的代码,发生这种情况是因为对于 table、

中的每个单元格
  1. 您创建了一个QuestionCategoryCellView

  2. QuestionCategoryCellView 创建一个 QuestionCategoryCellViewModel

  3. QuestionCategoryCellViewModel 的每个实例都有这个 属性:

    let coreDM: CoreDataManager = CoreDataManager()
    
  4. CoreDataManager 调用 persistentContainer.loadPersistentStores

因此,每个单元格都会加载持久容器。那永远不应该发生。您有四个单元格,每个单元格都有自己的持久容器,每个容器都有自己的模型副本。

您的 CoreDataManagerpersistentContainer 只需创建一次,然后用于每个单元格。我会说去单身 CoreDataManager 但两者都可能有效。

一个简单的方法是

  1. 将此行添加到 CoreDataManager:

    static var shared = CoreDataManager()
    
  2. 更改 QuestionCategoryCellViewModel,这样就不会创建 CoreDataManager,而是这样做:

    let coreDM: CoreDataManager = CoreDataManager.shared
    

如果你这样做,你就会避免这个问题。

然而,您将 运行 陷入代码中完全不同的错误。您的数据模型使用 [QuestionList] 作为可转换属性的类型。但是 QuestionList 不符合 NSCoding,因此应用程序崩溃时不起作用。在这里问一个不同的问题。