0

我一直在尝试解决这个问题一段时间,但无济于事。
从 UserNotification 自定义操作访问托管对象,然后尝试保存对此对象的更改时,我收到以下消息:

[错误] 错误:CoreData:错误:无法调用 NSManagedObject 类“NSManagedObject”上的指定初始化程序。

基本上,设置如下:
1. 用户收到通知
2. 选择自定义操作
3. UserNotification Center Delegate 从通知中的信息中提取对象的 URI,然后从持久存储中提取它
4. 完成后并进行类型转换,委托调用对象上的适当方法
5. 方法返回后委托尝试保存上下文,这就是错误出现的地方。

以下是一些相关代码:

// - UNUserNotification Centre delegate
extension HPBUserNotificationsHandler: UNUserNotificationCenterDelegate {    
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

    guard let object = getAssociatedObject(id: response.notification.request.identifier) else { return }

    switch response.actionIdentifier {
    case UNNotificationDismissActionIdentifier:
        ....
    case UNNotificationDefaultActionIdentifier:
        ....
    case HPBReminderAction.take.rawValue:
        // get intake object
        guard let reminder = object as? HPBIntake else { return }
        reminder.take(at: Date()) 
        try! dataController.saveContext() // here is when the error is raised
    default:
        break
    }

    completionHandler()
}

从持久存储中提取对象的函数:

func getAssociatedObject(id: String) -> NSManagedObject? {
    guard let psc = dataController.managedObjectContext.persistentStoreCoordinator else { return nil }
    guard let objURL = URL(string: id) else { return nil }
    guard let moid = psc.managedObjectID(forURIRepresentation: objURL) else { return nil }
    return dataController.managedObjectContext.object(with: moid)
}

如果我直接在应用程序中对此对象进行相同的更改 - 一切正常。所以我认为问题在于从用户通知的自定义操作中获取对象。但我无法弄清楚是什么问题。

这是一些附加信息。当我reminder在调用take(on:)函数之前检查对象时,它显示为错误:

Home_Pillbox.HPBIntake:0x7fb1a9074e90(实体:Intake;id:0x7fb1a9069e50 x-coredata:///Intake/tAC4BBCD4-B128-4C6F-8E1B-2EE7D4EDBCB34;数据:故障)

当然,当调用函数时,会触发错误,但对象未正确初始化,而是将所有属性填充为nil

Home_Pillbox.HPBIntake: 0x7fb1a9074e90 (entity: Intake; id: 0x7fb1a9069e50 x-coredata:///Intake/tAC4BBCD4-B128-4C6F-8E1B-2EE7D4EDBCB34 ; data: {dosage = 0; identifier = nil; localNotification = nil; log = nil ; 餐 = 0; medName = nil; notificationRequest = nil; profileName = nil; schedule = nil; status = 1; treatment = nil; unit = 0; userNotes = nil;})

因此,当上下文尝试保存它时,它不能像属性一样nil,这是数据模型所不允许的。还困扰我的是,错误提到了指定的初始化程序,NSManagedObject而不是子类的名称HPBIntake,即使对象的类型明显正确。

任何帮助将不胜感激。

编辑:这是saveContext()DataController 中函数的实现:

    func saveContext() throws {
        if managedObjectContext.hasChanges {
            do {
                try managedObjectContext.save()
            } catch let syserr as NSError {
                throw syserror
            }
        }
    }
4

2 回答 2

0

只是一个想法:你说

如果我直接在应用程序中对此对象进行相同的更改 - 一切正常。

我假设您在主线程上进行了这些更改。

您是否检查了是否userNotificationCenter(_:didReceive:withCompletionHandler:)也在主线程上执行?如果没有,您可能会遇到问题,因为核心数据预计仅在一个线程上执行。在这种情况下,您可以尝试执行此函数的主体

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
  DispatchQueue.main.async {
    // Your code here
  }
}

您还应该-com.apple.CoreData.ConcurrencyDebug 1在您的方案中设置启动参数以及异常断点:
在此处输入图像描述 然后当发生核心数据多线程冲突时,您的应用程序将停止。

于 2019-02-24T15:46:59.077 回答
0

另一个想法:由于您收到初始化程序错误,在我看来,您通过通知传输其 id 的对象在您尝试保存时尚未初始化。
此链接中,我发现以下内容:

如果没有为它接收到的对象标识符找到记录,object(with:) 将引发异常。例如,如果应用程序删除了与对象标识符对应的记录,Core Data 就无法将对应的记录交给您的应用程序。结果是一个例外。
existingObject(with:) 方法的行为方式类似。主要区别在于,如果该方法无法获取与对象标识符对应的托管对象,则会引发错误。

所以我建议在对bygetAssociatedObject(id: String)的调用中替换。如果这引发错误,则您知道相关对象尚不存在或不再存在。 如果您的应用程序允许这样做,您必须通过指定的初始化程序对其进行初始化object(with: moid)existingObject(with: moid)

init(entity entity: NSEntityDescription, insertIntoManagedObjectContext context: NSManagedObjectContext?)  

在您尝试存储它之前。

编辑:

这个答案中,您可以找到更多关于如何调试核心数据处理的建议。

于 2019-02-26T21:22:59.997 回答