通过UserNotification自定义操作访问时,CoreData无法初始化托管对象

时间:2019-02-22 17:17:54

标签: ios swift core-data

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

  

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

基本上,设置如下:
1.用户收到通知
2.选择一个自定义动作
3.从通知中的信息中,UserNotification Center代表提取对象的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(实体:Intake; id:0x7fb1a9069e50 x-coredata:/// Intake / tAC4BBCD4-B128-4C6F-8E1B-2EE7D4EDBCB34;数据:{剂量= 0;标识符=无; localNotification log =零;进餐= 0; medName =零; notificationRequest =零; profileName =零;日程表=零;状态= 1;治疗=零;单位= 0; userNotes =零;})

因此,当上下文尝试保存时,由于属性nil而无法保存,这是数据模型所不允许的。令我困扰的是,即使对象输入正确,错误也会提到NSManagedObject上指定的初始化程序,而不是子类HPBIntake的名称。

任何帮助将不胜感激。

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

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

2 个答案:

答案 0 :(得分: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以及异常断点:
enter image description here 然后,当发生核心数据多线程冲突时,您的应用将停止。

答案 1 :(得分:0)

另一个想法:由于收到初始化错误,在我看来,当您尝试保存通过通知传递其ID的对象时,该对象尚未初始化。 在this link中,我发现了以下内容:

  如果找不到该对象的记录,则

object(with :)会引发异常。   它收到的对象标识符。例如,如果应用程序已删除   与对象标识符相对应的记录,核心数据为   无法提交您的应用程序相应的记录。结果   是一个例外。
  existingObject(with :)方法的行为类似   时尚。主要区别在于,如果该方法抛出错误   无法获取与对象相对应的托管对象   标识符。

因此,我建议在getAssociatedObject(id: String)中将对object(with: moid)的调用替换为existingObject(with: moid)。如果这引发了错误,则说明相关对象尚不存在或不再存在。
如果您的应用允许这样做,则必须由指定的初始化程序

对其进行初始化
init(entity entity: NSEntityDescription, insertIntoManagedObjectContext context: NSManagedObjectContext?)  

在尝试存储它之前。

编辑:

this answer中,您可以找到有关调试核心数据处理的更多建议。