问题
iOS Core Data - Serious application error - attempt to insert nil
Hello,
My app runs actualy stable, but in seldom cases it crashes with this error message...
2019-04-02 20:48:52.437172+0200 myAppName[4422:1595677] [error] 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) 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) 2019-04-02 20:48:52.438246+0200 myAppName[4422:1595677] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFSet addObject:]: attempt to insert nil'
...when it tries to save the current context (this part in my code is still in 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]);
}
});
}
'seldom' means: Most Customers do never experience the issue, for few customers it happens several times per day. I was able to produce it 2 times during the last two days although I tried several ways to force this error (see below).
This is the setup:
- The respective data is in one Entity (table)
- A NSFetchedResultsController shows the data in an UITableView
- User can hit a button to add a new record.
- New record has only some basic data and initiates two API calls to two webservers
- Each webserver response does update the record
- After both are done (or were cancelled due to timeout), I call the saveChanges function from above only once.
- All functions use the same context created by NSPersistentContainer as follow (this part is already in 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
}()
If I could reproduce the error somehow, I could find an appropriate solution, but as it almost never happens, I'm stuck.
Do you have an idea how I could reproduce the error from above? Or do you have a clue what could cause the error in my case?
What I tried already to reproduce the error:
- Create hundereds of record
- Create hundereds of record in a few seconds
- Create hundereds of record during switching internet connection on / off / on / off /...
- Create hundereds of record during mixed from background and main thread (I removed the dispatch from saveChanges for that)
- Create hundereds of record with different delays on the API (added random sleep function on the webserver)
- Long time execution, the app run for 24 hours on a real device and created record each 2 minutes
- Mixes of all of them
回答1:
NSManagedObjects are restricted to a single queue. They are not thread-safe for reading or writing. Reading an NSManagedObject can cause a fault, which is a write operation. That means that NSManagedObjects retrieved from a main queue context (like viewContext
) cannot be passed to other queues.
The details of all of this are discussed in the Core Data Programming Guide:
NSManagedObject instances are not intended to be passed between queues. Doing so can result in corruption of the data and termination of the application. When it is necessary to hand off a managed object reference from one queue to another, it must be done through NSManagedObjectID instances.
The general approach with NSPersistentContainer is to use something like viewContext
exclusively on the main queue, and to use performBackgroundTask
to handle background operations, or you can use newBackgroundContext
to generate a background context, and use perform
or performAndWait
on it to manipulate objects that are fetched from that context.
Moving an object between contexts is done by fetching the same objectID
in the other context (keeping in mind that this will return a fresh instance from the store).
You can track down mistakes by adding -com.apple.CoreData.ConcurrencyDebug 1 to your scheme. When you do this, errors will immediately trap on the delightfully named __Multithreading_Violation_AllThatIsLeftToUsIsHonor__
.
来源:https://stackoverflow.com/questions/55517083/ios-core-data-serious-application-error-attempt-to-insert-nil-in-less-than