NSFetchedResultsController NSInternalInconsistencyException crash (yes another one, but now with a sample project) - uitableview

I get a variation of the following crash in my project:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (22) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (8 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
Soon I will be moving to diffable datasource API, but I'd really want to know what I am doing wrong here, and maybe I can release a bit more stable goodbye version to my iOS 12 users.
I have a Core Data setup with two contexts, a main and a background. The main merges data when the background saves, through notifications. Nothing groundbreaking here.
The NSFetchedResultsControllerDelegate updates the tableview with the beingUpdates/endUpdates animation section. I disabled the NSFetchedResultsChangeUpdate updates, so they won't interfere, to isolate the source of the problem.
It seems the app crashes when the updates are in progress and then the fetchedResultsController is loading by calling performFetch. The crash log is not much of a help in this case. Any idea where to look?
There are a couple of good sources e.g. this one, but it does not help in this case.
Thanks for any insights.
Here is the sample project: https://filebin.net/xtordf60rs4pmckk
The SampleSOLongerList.zip crashes all the time, but since the list is longer, it might be harder to debug.
Video of the crash: https://youtu.be/D7yoojdpkdQ
The crash happens only at fresh install (not all the time - if more data is preloaded, it happens all the time)
To crash the app, you just start it, and then hit Next, and it should crash most of the time.

Related

Can you set a breakpoint for a specific exception type?

We're trying to debug some unit tests which are randomly failing. We believe this is because the codebase improperly mixes UI interaction as part of unit testing (i.e. instantiates ViewControllers while firing off service requests that update them.)
I'm trying to go through and clean this up, but I keep getting things like this...
2019-01-15 11:05:37.500086-0500 [NAME REDACTED][78259:9742813] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to delete section 1, but there are only 0 sections before the update'
The trouble is we have no idea which test (or tests) is causing the table to be updated. Worse, we don't even know which table it is! The call stack improperly hangs on a WaitForExpectations call in a unit test which tests service-calls and responses and which doesn't even reference, let alone instantiate anything to do with the UI.
Our guess is its happening from an earlier test that fires up some UI component which internally fires off some request, and the response comes back long after the original unit test was finished. That would explain why it's breaking in a completely-unrelated test. But it's just a guess because we're not sure how to actually break on the line that's causing the true, underlying exception.
So as the title says, how can you break on a specific exception? I've tried symbolic breakpoints to no avail, and the regular All Exceptions breakpoint catches way too much to the point of being more intrusive than useful. We only want to break on that one exception. Is it possible?

Objects should not be both modified and additional

Part of a project I'm working in involves managing storages for products.
A Storage object contains StorageShelf objects, which define the amount of shelves. A StorageShelf, in turn, contains some other objects such as StorageLocation objects that determine the amount of products that can fit on a shelf. All These objects exist in CoreData. I also store Storages and StorageShelfs in a CloudKit database for synchronization purposes.
If I am running my app on two iPads, and add a storage with some shelves on one of the devices, it receives the notifications on the other and starts processing the updates it received.
I have a CloudOperationQueue in my app that has a maxConcurrentOperationCount of 1, in order to make sure only one update is made at the same time (This is because all objects of my app, also those out of the scope of this question are highly interdependent).
Inconsitently, this results in the following SIGABRT error message:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Objects should not be both modified and additional'
Specifically, this error occurs when I save the context after making modifications to one of the shelf objects I received. There is no consistency in which shelf.
Question
What does this error mean, and how can one debug it?
I recently got this error, also inconsistently. The issue was connected to adding an object and then saving the CoreData context on a background thread (part of a drag and drop update), while also trying to update a view showing a list of the objects on the main thread.
The fix is to cause things to happen synchronously, for me the easy fix was moving the save operation to the main thread, like (in Swift):
DispatchQueue.main.async {
// save your context
}
I was also getting this error recently, and also inconsistently. I had several core data objects with a 1:many relation and randomly when I would add an object to that relation and save context I would receive this exception.
For me the problem turned out to be I was using the NSPersistentContainer.viewcontext
in a background thread and
to write to core data
whehn according to these docs https://developer.apple.com/documentation/coredata/nspersistentcontainer viewcontext should be read only for the main queue. Switching to NSpersistentContainer.newBackgroundContext instead fixed the issue.

app gets crashed suddenly, error says: attempt to recursively call -save: on the context in core data

In my app, on button click,for every 1 second(timer1 call), I am inserting some data into the core data and for every 2 minutes(timer2 call) I am fetching inserted data to send it to the server(web service call).
Data insertion and retrieval is happening correctly but some times app is getting crashed and error says attempt to recursively call -save: on the context in core data.
is it fine to use single managedObjectContext for both fetching and insertion?
what concurrency type to be used for mangedObjectContext?(privateQueueConcurrency or mainQueueConcurrency)
[I am using Xcode 8.1 with IOS 10.1]
Hmm, you don't give us much to go on, a bit more code around how you store data might help. Are you using NSPersistentContainer? Do you wrap your operations in a context.perform() call?
To help locate where things go off the road, you might want to provide these two launching flags in your scheme:
-com.apple.CoreData.Logging.stderr 1
-com.apple.CoreData.ConcurrencyDebug 1

Data vanishing in Core Data

I'm getting crash reports from a large percentage of my user base, and have tracked it down to a core data entity having every record deleted somehow.
This entity is one where the app downloads every row the first time you launch the app, and then the table is never written to again, it's just a read only lookup table.
It shouldn't be possible for anything to ever be removed from the entity, but that's what I'm seeing. All reports are the app functions fine for a while (sometimes days, other times minutes after a fresh install) and then suddenly starts crashing any time you try to do anything.
I've gone over the entire code base and nothing ever deletes data from the entity. The bug only occurs in a version of the app just released. Rolling all users back to the previous release - only two weeks old, has fixed the bug for for everyone. It's an enterprise app, I simply redeployed the old binary.
I went over every change in source control and found nothing related to the entity.
In fact all code related to this entity has not changed at all in the last few years. It's been perfectly stable all that time.
Other data is persisted to disk in the same -[NSManagedObjectContext save:] call, and that data does not vanish.
I'm stumped. How can data in an entity be erased? How can I debug this issue further?
I am unable to reproduce the bug on my own devices, although I have extensive logging in place (and am happy to add more — this is an enterprise app only ever used for work so user privacy isn't an issue).

How to most efficiently (both memory & performance wise) load a uitableview with large number of records?

We have a web service iPhone app which fetches short texts (no graphics) in increment of 10 records - user can load more records by pressing 'load more' button. (akin to 'load 25 more' in default 'App Store' App)
Currently, NSXMLParser is used to parse the XML, the records are stored then in NSMutableArray, and new records (from 'load more') are appended to the array.
Now, we are experiencing two problems, which we believe are related to our current method of storing the records into the RAM. These are:
In low memory conditions, when we switch back to the App from app switcher, and then press 'back' button from the detail view, App either crashes or reloads the table view!
After having just 400 records loaded the app crashes!
To combat these issues, will, just storing the records into SQLite (or Core Data) instead of an array, do it? Or we need to do something more/different?
Please guide!
Thanks!
I am able to show more than 12000 records with sizable text & images in each row. I suspect you are having some other problem?
Have you profiled your code? Try Instruments. Also when you launch & come back from App switcher, how are you handling viewWillDisappear and viewDidDisappear? Are you dealloc or release something when you switch views?
You should override didReceiveMemoryWarning: in your view controller if you do not want the view deallocated when a memory warning occurs. It could be due to that. As well as that, your viewDidLoad method should recreate the view state ensuring that no interface element is left deallocated or nil.
If you allocate too much memory in one go, the iOS Watchdog service will kill your app because it suspects the app will cause the amount of memory allocated to reach 100% (after which, bad things happen). It's also possible that if you use the UITableView methods to add extra records onto the end of the list (insertRowsAtIndexPaths:withRowAnimation:) it could be throwing out an NSInconsistencyException under heavy and/or repeated usage. If your loading methods are asynchronous, then maybe it just wasn't ready in time or there was a network failure, and the insert call for your array didn't insert the full number of rows, which will crash the app if the number of rows doesn't match your data source method numberOfRowsInSection:.
resolved it... after reading #Steven's comment... I got convinced that it was not memory issue... so, I digged further, & found the issue.
Actually, I have a deselectRowAtIndexPath method in tableview. And in low memory environment, iPhone releases the records stored in memory... so, in that situation, deselectRowAtIndexPath was pointing to nothing, which was causing the crash... modified & resolved it now!
Thanks!

Resources