iOS 核心数据 - 严重的应用程序错误 - 尝试插入零 - 不到 1%

Posted

技术标签:

【中文标题】iOS 核心数据 - 严重的应用程序错误 - 尝试插入零 - 不到 1%【英文标题】:iOS Core Data - Serious application error - attempt to insert nil - in less than 1% 【发布时间】:2019-04-04 13:22:43 【问题描述】:

ios 核心数据 - 严重的应用程序错误 - 尝试插入 nil

你好,

我的应用运行实际上很稳定,但在极少数情况下它会崩溃并显示此错误消息...

2019-04-02 20:48:52.437172+0200 myAppName[4422:1595677] [错误] 错误:严重的应用程序错误。在核心数据更改处理期间捕获到异常。这通常是 NSManagedObjectContextObjectsDidChangeNotification 观察者中的一个错误。 -[__NSCFSet addObject:]: 尝试用 userInfo (null) 插入 nil CoreData:错误:严重的应用程序错误。在核心数据更改处理期间捕获到异常。这通常是 NSManagedObjectContextObjectsDidChangeNotification 观察者中的一个错误。 -[__NSCFSet addObject:]: 尝试用 userInfo (null) 插入 nil 2019-04-02 20:48:52.438246+0200 myAppName[4422:1595677] *** 由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“-[__NSCFSet addObject:]:尝试插入 nil”

...当它试图保存当前上下文时(我代码中的这部分仍在 objc 中):

- (void)saveChanges

    dispatch_async(dispatch_get_main_queue(), ^
        NSError *err = nil;
        BOOL succesful = [self->context save:&err];
        if (!succesful)
        
            NSLog(@"ERROR MESSAGE DURING SAVING CONTEXT: %@", [err localizedDescription]);
        
    );

'很少'的意思是: 大多数客户从来没有遇到过这个问题,对于少数客户来说,它每天会发生几次。 尽管我尝试了几种方法来强制出现此错误(见下文),但我在过去两天内能够生成 2 次。

这是设置:

各自的数据在一个实体(表)中 NSFetchedResultsController 在 UITableView 中显示数据 用户可以点击按钮添加新记录。 新记录只有一些基本数据,并向两个网络服务器发起两次 API 调用 每个网络服务器响应都会更新记录 在两者都完成后(或由于超时而取消),我只从上面调用 saveChanges 函数一次。 所有函数都使用由 NSPersistentContainer 创建的相同上下文,如下所示(这部分已经在 swift 中)
@objc lazy var persistentContainer: NSPersistentContainer = 

        let container = NSPersistentContainer(name: "myAppName")
        let description = NSPersistentStoreDescription(url: SomeHelper.urlForFileInDocFolder("storev7.data"))
        container.persistentStoreDescriptions = [description]

        container.loadPersistentStores(completionHandler:  (storeDescription, error) in
            if let error = error as NSError? 
                fatalError("Unresolved error \(error), \(error.userInfo)")
            
        )

        return container
 ()

如果我能以某种方式重现错误,我可以找到合适的解决方案,但由于它几乎从未发生过,所以我被卡住了。

您知道如何从上面重现错误吗?或者您是否知道在我的案例中可能导致错误的原因是什么?

我已经尝试过重现错误:

创建数百条记录 在几秒钟内创建数百条记录 在打开/关闭/打开/关闭互联网连接期间创建数百条记录/... 在从后台和主线程混合期间创建数百条记录(为此,我从 saveChanges 中删除了调度) 在 API 上创建数百条具有不同延迟的记录(在网络服务器上添加了随机休眠功能) 长时间执行,应用在真机上运行24小时,每2分钟创建记录 所有这些的混合

【问题讨论】:

这个concurrencyType是什么? -save 的调用者是在主队列还是另一个队列?该队列是否以任何方式修改上下文? @RobNapier 我希望它是NSMainQueueConcurrencyType,但我没有定义它。我只是使用NSPersistentContainer 如上所示通过使用persistentContainer.viewContext 创建上下文。我还将所有-save 请求发送到主线程。并且在最初创建上下文后,我不会更改上下文。 viewContext 好;匹配。您是否对主队列以外的任何队列上的任何记录进行了更改(不仅仅是保存;任何更改。NSManagedObjects 不是线程安全的,甚至不用于读取。) @RobNapier 仅适用于当前对象,我使用[NSEntityDescription insertNewObjectForEntityForName:@"MyEntityName" inManagedObjectContext:context]; 创建了一个新对象,之后它从主线程和后台线程(来自 web api 调用)更改,最后我调用一次context save 在主线程中。 【参考方案1】:

NSManagedObjects 仅限于单个队列。它们对于读取或写入不是线程安全的。读取 NSManagedObject 可能会导致错误,即写入操作。这意味着从主队列上下文(如viewContext)检索到的 NSManagedObjects 不能传递给其他队列。

所有这一切的细节都在Core Data Programming Guide中讨论:

NSManagedObject 实例不打算在队列之间传递。这样做可能会导致数据损坏和应用程序终止。当需要将托管对象引用从一个队列传递到另一个队列时,必须通过 NSManagedObjectID 实例来完成。

使用 NSPersistentContainer 的一般方法是在主队列上独占使用viewContext 之类的东西,并使用performBackgroundTask 处理后台操作,或者您可以使用newBackgroundContext 生成后台上下文,并使用@987654327 @ 或 performAndWait 以操作从该上下文中获取的对象。

在上下文之间移动对象是通过在另一个上下文中获取相同的objectID 来完成的(请记住,这将从存储中返回一个新实例)。

You can track down mistakes by adding -com.apple.CoreData.ConcurrencyDebug 1 to your scheme. 执行此操作时,错误将立即出现在命名为 __Multithreading_Violation_AllThatIsLeftToUsIsHonor__ 上。

【讨论】:

非常感谢您。对我来说完全有意义!还有一个问题:如果我遵循这种方法,并创建两个专用的私有上下文来使用 Web 服务器 API 调用的响应来操作对象,那么我可以调用 -save 3 次吗?在初始创建后从主线程一次,在预先使用existingObjectWithID:error 加载对象后,在私有上下文中两次在performBackgroundTask 中? 也就是说,如果两个或多个-save同时发生,只要每个都有自己的上下文就没有问题? 如果没有冲突,那么就没有问题。但是如果有冲突,它会抛出一个错误。只要每个上下文在自己的队列中管理自己的对象,它就旨在管理并发。把它想象成数据库 COMMIT。 冲突意味着在不同队列的一行上写入/读取相同的属性(列),对吧?从不同队列读取/写入同一行的不同属性是可以的,对吧? 不,任何修改默认都会引起冲突。请参阅mergePolicy 了解如何设置覆盖,但如果您对类似这样的相同对象进行大量工作,您可能应该将所有修改工作分派到单个后台上下文以序列化更改。我不相信有任何看起来像“在每个属性冲突上抛出错误”的合并策略。您可以自动合并每个属性的冲突,但会因总对象版本不匹配而产生错误。 (也就是说,您总是可以只检查冲突字典并自己决定如何合并数据。)

以上是关于iOS 核心数据 - 严重的应用程序错误 - 尝试插入零 - 不到 1%的主要内容,如果未能解决你的问题,请参考以下文章

CoreData:错误:严重的应用程序错误。在核心数据更改处理期间捕获到异常

controllerDidChangeContent 处的核心数据严重应用程序错误

核心数据:NSFetchedResultsController 错误:尝试创建两个动画

CoreData:错误:严重的应用程序错误。在核心数据更改处理期间捕获到异常

无法保存核心数据

iOS Swift 3核心数据-尝试递归调用-save的致命错误:上下文中止