I have a Core Data app that displays a large amount of instances in an UITableView
.
We have a mainQueueContext
running in the main thread and a privateQueueContext
running in a background thread.
We load the data in the UITableView without problems, but when we load a refreshed version from the API we save it to the privateQueueContext
and merge it in the mainQueueContext
[self.mainQueueContext mergeChangesFromContextDidSaveNotification:notification];
It takes a few seconds to trigger the call to
-(void)controllerDidChangeContent:(NSFetchedResultsController *)controller
Which trigger the call to:
[self.tableView endUpdates];
Then the App hangs and CPU goes to 100% and memory begins to rise indefinitely until it runs out of memory. (see picture below)
With less than 1000 elements this problem does not happen. I was wondering if any of you saw something like this.
Here's some data from instruments
One improvement you can do is to use fetchBatchSize.
From Apple Doc"
When the fetch is executed, the entire request is evaluated and the identities of all matching objects recorded, but no more than batchSize objects’ data will be fetched from the persistent store at a time. The array returned from executing the request will be a proxy object that transparently faults batches on demand. (In database terms, this is an in-memory cursor.)
You could also use fetchLimit and fetchOffset to implement pagination.
Cheers, Alessandro
I have experienced this problem too. I ended up gathering all updated into array on controller: didChangeObject:
recording index path, NSFetchedResultsChangeType, and object itself. On controllerDidChangeContent:
I was looking if its count is larger than X, if it is then just reload whole table (it's much more efficient) and if it's not then apply all changes normally as if it was happening in didChangeObject:
method. It just doesn't make sense to insert/delete/move so much cells, as user won't see most of these changes anyway.
I hope this approach will help you!
I faced a similar problem. In my case the solution was to remove setting explicitly rowHeight
of the tableView to UITableViewAutomaticDimension
:
tableView.rowHeight = UITableViewAutomaticDimension
I used instead the delegate tableView(_:heightForRowAt:)
Although the documentation of rowHeight seems to state the opposite:
There are performance implications to using tableView(:heightForRowAt:) instead of rowHeight. Every time a table view is displayed, it calls tableView(:heightForRowAt:) on the delegate for each of its rows, which can result in a significant performance problem with table views having a large number of rows (approximately 1000 or more).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With