调试核心数据 __NSCFSet addObject nil 异常
Debugging Core Data __NSCFSet addObject nil exception
我在单元测试期间在 Core Data 线程上收到异常抛出消息:
CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. -[__NSCFSet addObject:]: attempt to insert nil with userInfo (null)
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFSet addObject:]: attempt to insert nil'
*** First throw call stack:
(
0 CoreFoundation 0x00683a14 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x02334e02 objc_exception_throw + 50
2 CoreFoundation 0x0068393d +[NSException raise:format:] + 141
3 CoreFoundation 0x005595b9 -[__NSCFSet addObject:] + 185
4 CoreData 0x001d47c0 -[NSManagedObjectContext(_NSInternalChangeProcessing) _processPendingInsertions:withDeletions:withUpdates:] + 560
5 CoreData 0x001cee8a -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] + 2410
6 CoreData 0x001ce506 -[NSManagedObjectContext processPendingChanges] + 54
7 CoreData 0x001f359b developerSubmittedBlockToNSManagedObjectContextPerform + 443
我正在尝试确定是什么原因造成的,但由于它发生在 NSManagedObjectContext
队列中,因此该线程没有任何我自己的代码的堆栈跟踪。
我在 -[__NSCFSet addObject:]
和 -[NSManagedObjectContext processPendingChanges]
上设置了符号断点,但停在那里时看不到任何状态,这有助于我确定是哪些对象导致了问题。
我想到的下一步是尝试调配 -[__NSCFSet addObject:]
以添加我自己的实现,这样我就可以仅在参数为 nil 时停止。希望该集合不为空,并且我可以在插入 nil 之前查看其内容来获取更多信息。不过,我 运行 很难调整它,因为它是私有的 class。
使用上面的方法,或者我没有考虑过的方法,我如何才能获得有关导致异常的原因的更多信息?
感谢@bteapot 建议我将 -com.apple.CoreData.ConcurrencyDebug 1
参数添加到我的方案中。我从 Ole Begemann 的优秀 Core Data Concurrency Debugging 文章中获得了有关其工作原理的更多信息。
添加此标志会导致在您的代码从不正确的线程调用您的 NSManagedObjectContext
时立即抛出异常。这在 Xcode 中非常有效,但请注意,在 Xcode 机器人中,这会导致测试失败并显示以下无用消息:
Lost connection to test manager service
我遇到了同样的问题,今天一整天都在努力解决这个问题。添加并发调试对我来说是朝着正确方向迈出的一步,但潜在的问题是我试图从不同线程访问托管对象上下文的同一实例。导致我崩溃的方法是试图 return 一个获取的对象。我能够通过将我的 do、try、catch 块包装在 context.perform 块中并使该方法将转义闭包作为参数而不是 returning 对象来修复它。
func fetchUser(id: Int, completion: @escaping (User) -> ()) {
let predicate = NSPredicate(format: "id = %d", id)
let request: NSFetchRequest<User> = User.fetchRequest()
request.predicate = predicate
context.perform {
// try to fetch user from core data
do {
if let user = try self.context.fetch(request).first {
completion(user)
} else {
// if no user exists with that id, create and return a new user
self.seedAutoPopulatingLists(for: id)
completion(NSEntityDescription.insertNewObject(forEntityName: "User", into: self.context) as! User)
}
} catch let error as NSError {
print("Could not fetch: \(error) \(error.userInfo)")
}
}
}
希望这对以后 运行 的任何人有所帮助。
在swift5
创建持久性容器
private final class PersistanceContainerProvider {
var container: NSPersistentContainer = {
let container = NSPersistentContainer(name: "Test")
container.loadPersistentStores(completionHandler: { storeDescription, error in
if let error = error as NSError? {
fatalError("Unable to load persistance store")
}
})
return container
}()
}
已创建具有后台并发性的 ManagedContext
private init(persistentContainerProvider: DefaultPersistanceContainerProvider = PersistanceContainerProvider()){
self.persistentContainer = persistentContainerProvider.container
self.managedObjectContext = persistentContainer.newBackgroundContext()
print("Wasim \(self.managedObjectContext.concurrencyType.rawValue)")
}
应用managedObjectContext.performAndWait
func addTargetCode(symbol: Symbol) {
managedObjectContext.performAndWait {
do {
if let existing = try managedObjectContext.fetch(ManagedTargetCurrency.fetchRequest(by: symbol.code)).first {
existing.code = symbol.code
existing.name = symbol.name
try save()
} else {
let added = (NSEntityDescription.insertNewObject(forEntityName: ManagedTargetCurrency.entityName, into: managedObjectContext) as? ManagedTargetCurrency).require(hint: "Wrong Core Data Configuration?")
added.code = symbol.code
added.name = symbol.name
try save()
}
} catch let error {
print(Errors.addError(cause: error))
}
}
}
我在单元测试期间在 Core Data 线程上收到异常抛出消息:
CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. -[__NSCFSet addObject:]: attempt to insert nil with userInfo (null)
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFSet addObject:]: attempt to insert nil'
*** First throw call stack:
(
0 CoreFoundation 0x00683a14 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x02334e02 objc_exception_throw + 50
2 CoreFoundation 0x0068393d +[NSException raise:format:] + 141
3 CoreFoundation 0x005595b9 -[__NSCFSet addObject:] + 185
4 CoreData 0x001d47c0 -[NSManagedObjectContext(_NSInternalChangeProcessing) _processPendingInsertions:withDeletions:withUpdates:] + 560
5 CoreData 0x001cee8a -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] + 2410
6 CoreData 0x001ce506 -[NSManagedObjectContext processPendingChanges] + 54
7 CoreData 0x001f359b developerSubmittedBlockToNSManagedObjectContextPerform + 443
我正在尝试确定是什么原因造成的,但由于它发生在 NSManagedObjectContext
队列中,因此该线程没有任何我自己的代码的堆栈跟踪。
我在 -[__NSCFSet addObject:]
和 -[NSManagedObjectContext processPendingChanges]
上设置了符号断点,但停在那里时看不到任何状态,这有助于我确定是哪些对象导致了问题。
我想到的下一步是尝试调配 -[__NSCFSet addObject:]
以添加我自己的实现,这样我就可以仅在参数为 nil 时停止。希望该集合不为空,并且我可以在插入 nil 之前查看其内容来获取更多信息。不过,我 运行 很难调整它,因为它是私有的 class。
使用上面的方法,或者我没有考虑过的方法,我如何才能获得有关导致异常的原因的更多信息?
感谢@bteapot 建议我将 -com.apple.CoreData.ConcurrencyDebug 1
参数添加到我的方案中。我从 Ole Begemann 的优秀 Core Data Concurrency Debugging 文章中获得了有关其工作原理的更多信息。
添加此标志会导致在您的代码从不正确的线程调用您的 NSManagedObjectContext
时立即抛出异常。这在 Xcode 中非常有效,但请注意,在 Xcode 机器人中,这会导致测试失败并显示以下无用消息:
Lost connection to test manager service
我遇到了同样的问题,今天一整天都在努力解决这个问题。添加并发调试对我来说是朝着正确方向迈出的一步,但潜在的问题是我试图从不同线程访问托管对象上下文的同一实例。导致我崩溃的方法是试图 return 一个获取的对象。我能够通过将我的 do、try、catch 块包装在 context.perform 块中并使该方法将转义闭包作为参数而不是 returning 对象来修复它。
func fetchUser(id: Int, completion: @escaping (User) -> ()) {
let predicate = NSPredicate(format: "id = %d", id)
let request: NSFetchRequest<User> = User.fetchRequest()
request.predicate = predicate
context.perform {
// try to fetch user from core data
do {
if let user = try self.context.fetch(request).first {
completion(user)
} else {
// if no user exists with that id, create and return a new user
self.seedAutoPopulatingLists(for: id)
completion(NSEntityDescription.insertNewObject(forEntityName: "User", into: self.context) as! User)
}
} catch let error as NSError {
print("Could not fetch: \(error) \(error.userInfo)")
}
}
}
希望这对以后 运行 的任何人有所帮助。
在swift5
创建持久性容器
private final class PersistanceContainerProvider {
var container: NSPersistentContainer = {
let container = NSPersistentContainer(name: "Test")
container.loadPersistentStores(completionHandler: { storeDescription, error in
if let error = error as NSError? {
fatalError("Unable to load persistance store")
}
})
return container
}()
}
已创建具有后台并发性的 ManagedContext
private init(persistentContainerProvider: DefaultPersistanceContainerProvider = PersistanceContainerProvider()){
self.persistentContainer = persistentContainerProvider.container
self.managedObjectContext = persistentContainer.newBackgroundContext()
print("Wasim \(self.managedObjectContext.concurrencyType.rawValue)")
}
应用managedObjectContext.performAndWait
func addTargetCode(symbol: Symbol) {
managedObjectContext.performAndWait {
do {
if let existing = try managedObjectContext.fetch(ManagedTargetCurrency.fetchRequest(by: symbol.code)).first {
existing.code = symbol.code
existing.name = symbol.name
try save()
} else {
let added = (NSEntityDescription.insertNewObject(forEntityName: ManagedTargetCurrency.entityName, into: managedObjectContext) as? ManagedTargetCurrency).require(hint: "Wrong Core Data Configuration?")
added.code = symbol.code
added.name = symbol.name
try save()
}
} catch let error {
print(Errors.addError(cause: error))
}
}
}