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 错误:尝试创建两个动画