Отладка основных данных __NSCFSet addObject nil исключение
Я получаю исключения во время моих модульных тестов в потоке Core Data с этим сообщением:
CoreData: ошибка: серьезная ошибка приложения. Исключение было обнаружено при обработке изменений базовых данных. Обычно это ошибка в наблюдателе NSManagedObjectContextObjectsDidChangeNotification. -[__NSCFSet addObject:]: попытка вставить nil с 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. Однако я столкнулся с трудностями, пытаясь справиться с этим, поскольку это частный класс.
Используя описанный выше подход, который я не рассматривал, как я могу получить больше информации о причинах исключения?
2 ответа
Спасибо @bteapot за предложение добавить -com.apple.CoreData.ConcurrencyDebug 1
аргумент в мою схему. Я получил больше информации о том, как это работает, из превосходной статьи Оле Бегемана об отладке параллельных данных.
Добавление этого флага вызывает исключение, как только ваш код вызывает ваш NSManagedObjectContext
из неверной ветки. Это прекрасно работает в Xcode, но имейте в виду, что в Xcode Bot это приводит к сбою тестов с этим бесполезным сообщением:
Потеряна связь с сервисом менеджера тестирования
У меня была такая же проблема, и я боролся с ней сегодня весь день. Добавление отладки параллелизма было для меня шагом в правильном направлении, но основная проблема заключалась в том, что я пытался получить доступ к одному и тому же экземпляру контекста управляемого объекта из разных потоков. Метод, вызвавший мой сбой, пытался вернуть извлеченный объект. Я смог исправить это, поместив блок do, try, catch в блок context.perform и заставив метод использовать экранирующее замыкание в качестве параметра вместо возврата объекта.
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)")
}
}
}
Надеюсь, это поможет любому, кто столкнется с этим в будущем.
В быстром 5
Создать контейнер сохраняемости
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))
}
}
}