Core Data实体关系触发NSUnknownKeyException常规排查无效求助
问题描述
我开发的应用使用Core Data实现数据持久化,采用MVVM架构以方便测试。
开发环境为搭载M1 Max芯片的MacBook Pro,运行macOS 12.4系统与Xcode 14 beta 2。
此前项目运行一切正常,直到我尝试在数据模型中新增一个与其他实体存在多对一relationship的entity:实例化该实体时会抛出指向关系定义属性的NSUnknownKeyException,我从模型中删除该关系后重新运行测试,意外发现错误仍然存在,尝试以下所有操作后均无法解决问题:
- 清理构建文件夹
- 删除派生数据,重启
Xcode - 重命名报错实体(错误仍存在,仅随实体名变化对应更新)
- 删除问题实体后重新添加到数据模型
- 关闭代码生成功能,手动编写NSManagedObject子类
- 检查Core Data模型XML文件,无异常残留属性可引发该问题
- 从模拟器中删除应用
- 抹除模拟器所有设置后重启
- 卸载后重新安装Xcode
我目前唯一未尝试的方案是使用Xcode 13运行代码(由于是新设备,我直接安装了beta版本,这可能是问题诱因),后续会尝试该方案。
当前判断是和Core Data模型、实体间关系相关的异常缓存存在于无法定位的路径,也有可能是beta版工具链本身的bug,该无法清除的错误导致开发无法继续推进,希望能得到相关排查思路和指导。
相关参考截图
- Prescription实体的Xcode模型界面

- Procedure实体的Xcode模型界面

- Prescription实体对应的.xcdatamodeld XML内容

- 测试过程中的报错信息

报错托管对象子类扩展(便利方法实现)
extension Prescription { // MARK: - INTERNAL TYPES enum PointOfCareContext: String, CustomStringConvertible { case premedication, perioperative, postoperative, hospitalised var description: String { return self.rawValue.capitalized } } // MARK: - PROPERTIES var drug: Drug { get { guard let drug = cd_drug else { fatalError("Persistent store corrupted: Prescription is missing relationship to Drug.") } return drug } set { cd_drug = newValue } } var timePrescribed: Date { get { guard let timePrescribed = cd_timePrescribed else { fatalError("Persistent store corrupted: Prescription is missing timePrescribed.") } return timePrescribed } set { cd_timePrescribed = newValue } } /// When the drug should be administered /// Passing nil to this property indicates the drug should be given asap (so will return current Date) var timeScheduled: Date? { get { guard let timeScheduled = cd_timeScheduled else { return Date.now } return timeScheduled } set { cd_timeScheduled = newValue } } /// When the drug was administered /// Nil indicates not given yet var timeAdministered: Date? { get { return cd_timeAdministered } set { cd_timeScheduled = newValue } } var doseRate: Measurement<UnitDensity> { get { if (cd_doseRate != 0) { fatalError("Persistent store corrupted: Prescription doseRate == 0.") } return Measurement(value: cd_doseRate, unit: .milligramsPerKilogram) } set { cd_doseRate = newValue.converted(to: .milligramsPerKilogram).value } } var dose: Measurement<UnitMass> { get { return Measurement(value: cd_dose, unit: .milligrams) } set { cd_doseRate = newValue.converted(to: .milligrams).value } } var doseVolume: Measurement<UnitVolume> { get { return Measurement(value: cd_doseVolume, unit: .milliliters) } set { cd_doseVolume = newValue.converted(to: .milliliters).value } } var context: PointOfCareContext { get { guard let contextValue = cd_context, let context = PointOfCareContext(rawValue: contextValue) else { fatalError("Persistent store corrupted: Prescription has an invalid administration context [\(cd_context ?? "")]") } return context } set { cd_context = newValue.rawValue } } }
测试对应的视图模型代码
extension ProcedurePlanView { class ViewModel: NSObject, ObservableObject, NSFetchedResultsControllerDelegate { // swiftlint:disable:next nesting typealias Dependencies = HasParameterController & HasPersistence & HasViewFactory private let parameterController: ParameterController private let persistenceService: PersistenceService private let viewFactory: ViewFactory private let frc: NSFetchedResultsController<Prescription> let patient: Patient let procedure: Procedure // MARK: - TabOne conformance @Published var clinician: String = "" @Published var anaesthetist: String = "" @Published var assistant: String = "" @Published var history: String = "" @Published var tasks: String = "" @Published var complications: String = "" @Published var mucousMembraneColor: MucousMembraneColor? @Published var asaGrade: ASAGrade? @Published var cannulaPosition: CannulaPosition? @Published var cannulaSide: Side? @Published var cannulaGauge: PeripheralCannulaGauge? @Published var auscultation: String = "" @Published var temperament: Double? @Published var airwayType: AirwayType? @Published var airwaySize: String? @Published var airwayCuffStatus: Bool? @Published var breathingSystem: BreathingSystemType? @Published var prescriptions: [Prescription]? // MARK: - INIT init(procedure: Procedure? = nil, patient: Patient, dependencies: Dependencies) { self.persistenceService = dependencies.persistenceService self.parameterController = dependencies.parameterController self.viewFactory = dependencies.viewFactory var currentProcedure: Procedure? if let procedure { currentProcedure = procedure } else { currentProcedure = Procedure(context: persistenceService.viewContext) currentProcedure!.patient = patient } let frc = ProcedurePlanView.ViewModel.createPremedFRCUsing( patient, procedure: currentProcedure!, context: dependencies.persistenceService.viewContext ) self.frc = frc self.patient = patient self.procedure = currentProcedure! self.prescriptions = ProcedurePlanView.ViewModel.performInitialFetchFor(frc) super.init() self.frc.delegate = self } // MARK: - PUBLIC METHODS func makeParameterGridView() -> some View { viewFactory.makeParameterGridView(patient: patient) } func resetForm() { clinician.removeAll() anaesthetist.removeAll() assistant.removeAll() history.removeAll() tasks.removeAll() complications.removeAll() mucousMembraneColor = nil asaGrade = nil cannulaPosition = nil cannulaSide = nil cannulaGauge = nil auscultation.removeAll() temperament = nil airwayType = nil airwaySize = nil airwayCuffStatus = nil breathingSystem = nil } func save() { procedure.patient = patient procedure.clinican = clinician procedure.anaesthetist = anaesthetist procedure.assistant = assistant procedure.history = history procedure.tasks = tasks procedure.complications = complications procedure.asaGrade = asaGrade Logger.viewModel.warning("Incomplete implementaton of cannula data in Procedure managed object and VM") _ = Observation.create( in: persistenceService.viewContext, patient: patient, parameter: parameterController.getParameterWith(name: .mucousMembraneColor), stringValue: mucousMembraneColor?.rawValue ?? "Unknown" ) do { try persistenceService.viewContext.saveIfNeeded() } catch { fatalError( "Unable to save context \(persistenceService.viewContext) to store due to error: \(error.localizedDescription)" ) } } // MARK: - PRIVATE METHODS private static func createPremedFRCUsing(_ patient: Patient, procedure: Procedure, context: NSManagedObjectContext) -> NSFetchedResultsController<Prescription> { let request = Prescription.fetchRequest() request.predicate = NSPredicate(format: "procedure == %@", procedure) request.sortDescriptors = [NSSortDescriptor(keyPath: \Prescription.cd_drug?.cd_nameGeneric, ascending: false)] let frc = NSFetchedResultsController( fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil ) return frc } private static func performInitialFetchFor(_ frc: NSFetchedResultsController<Prescription>) -> [Prescription] { do { try frc.performFetch() } catch { Logger.persistenceService.critical( "FRC failed to fetch Drugs from persistent store: \(error.localizedDescription)" ) } return frc.fetchedObjects ?? [] } // MARK: - NSFetchedResultsControllerDelegate conformance internal func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { let newPrescriptions = controller.fetchedObjects as? [Prescription] ?? [] prescriptions = newPrescriptions } } }
内容的提问来源于stack exchange,提问作者rustproofFish
相关产品推荐
相关产品推荐

