title: Core Data poor performance on save: and [UITableView endUpdates]
body: <p>I am having some performance issues with Core Data and I was hoping someone could give me some tips on how to improve it.  When calling <code>save:</code> for my <code>NSManagedObjectContext</code>, I am experiencing save times of over 1 second for a single entity.  I am performing the save on the main thread, so it is locking up the GUI during that time, which is unacceptable.  Here is my code that is doing the save; this code is initiated after modifying a 'description' field for a <code>WTSRecord</code>:</p>
  
  <pre><code>- (void)saveRecord:(WTSRecord *)record
             success:(void (^)(WTSRecord *record))success
             failure:(void (^)(NSError *error))failure {
      DDLogVerbose(@"saveRecord: %@", record.objectID);
  
      if (record.recordId != nil) {
          //mark this record as a pending modify if it has a database id
          record.pendingModify = [NSNumber numberWithBool:YES];
      }
      NSError *error;
      NSManagedObjectContext *context = record.managedObjectContext;
      NSTimeInterval startTime = [[NSDate date] timeIntervalSince1970];
      if (![context saveToPersistentStore:&amp;error]) {
          failure(error);
      } else {
          NSLog(@"time to persist record: %f", ([[NSDate date] timeIntervalSince1970] - startTime));
  
          ...do other stuff here...
  
      }
  }
  </code></pre>
  
  <p>I turned on SQL debug, and Core Data is just updating one record and doesn't appear to be doing anything out of the ordinary:</p>
  
  <pre><code>2014-08-13 11:25:32.528 Identify[5395:60b] CoreData: sql: BEGIN EXCLUSIVE
  2014-08-13 11:25:32.530 Identify[5395:60b] CoreData: sql: UPDATE ZWTSRECORD SET ZDESC = ?, Z_OPT = ?  WHERE Z_PK = ? AND Z_OPT = ?
  2014-08-13 11:25:32.531 Identify[5395:60b] CoreData: details: SQLite bind[0] = "ffffffffffffuuuuiiiiuu"
  2014-08-13 11:25:32.532 Identify[5395:60b] CoreData: details: SQLite bind[1] = (int64)48
  2014-08-13 11:25:32.533 Identify[5395:60b] CoreData: details: SQLite bind[2] = (int64)306
  2014-08-13 11:25:32.534 Identify[5395:60b] CoreData: details: SQLite bind[3] = (int64)47
  2014-08-13 11:25:32.535 Identify[5395:60b] CoreData: sql: COMMIT
  2014-08-13 11:25:32.538 Identify[5395:60b] time to persist record: 1.376321
  </code></pre>
  
  <p>This seems like a very simple update and really shouldn't take that long.  There are around 40 <code>WTSRecord</code>s in the sqllite database in this scenario.  If I delete the sqllite store and am just modifying one <code>WTSRecord</code>, the time to persist is less than a 1/20th of a second.</p>
  
  <p>I saw <a href="http://www.cocoanetics.com/2012/07/multi-context-coredata/" rel="nofollow noreferrer">this post</a> on cocoanetics about asynchronous saving, but I just want to know first if I am doing something fundamentally wrong before I go down that route.  Thanks in advance!</p>
  
  <p><strong>EDIT 1:</strong></p>
  
  <p>Attached is a screenshot of the Time Profiler.  It looks like a 1.3 seconds is dedicated to running the <code>controllerDidChangeContent:</code> in my <code>UITableViewController</code>, which is drawing table view cells.  Why would this be taking that long??</p>
  
  <p><img src="https://i.stack.imgur.com/xRwjw.png" alt="Time Profiler"></p>
  
  <p><strong>EDIT 2</strong></p>
  
  <p>Here are my <code>NSFetchedResultsControllerDelegate</code> methods.  They didn't really change from the boilerplate Apple code:</p>
  
  <pre><code>- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
  {
      DDLogVerbose(@"FRC calling beginUpdates in controllerWillChangeContent");
      [self.tableView beginUpdates];
      DDLogVerbose(@"FRC done calling beginUpdates in controllerWillChangeContent");
  }
  
  - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id &lt;NSFetchedResultsSectionInfo&gt;)sectionInfo
             atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
  {
      switch(type) {
          case NSFetchedResultsChangeInsert:
              DDLogVerbose(@"FRC inserted section");
              [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
              break;
  
          case NSFetchedResultsChangeDelete:
              DDLogVerbose(@"FRC deleted section");
              [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
              break;
      }
  }
  
  - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
         atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
        newIndexPath:(NSIndexPath *)newIndexPath
  {
      UITableView *tableView = self.tableView;
  
      switch(type) {
          case NSFetchedResultsChangeInsert:
              DDLogVerbose(@"FRC inserted object");
              [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
              break;
  
          case NSFetchedResultsChangeDelete:
              DDLogVerbose(@"FRC deleted object");
              [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
              break;
  
          case NSFetchedResultsChangeUpdate:
              DDLogVerbose(@"FRC updated object");
              [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
              break;
  
          case NSFetchedResultsChangeMove:
              DDLogVerbose(@"FRC moved objects");
              [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
              [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
              break;
      }
  }
  
  - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
  {
      DDLogVerbose(@"FRC calling endUpdates in controllerDidChangeContent");
      [self.tableView endUpdates];
      DDLogVerbose(@"FRC done calling endUpdates in controllerDidChangeContent");
  }
  </code></pre>
  
  <p>I added in some printouts and found that the vast majority of the time is spent in <code>[self.tableView endUpdates]</code>.  Here is a printout of my logs:</p>
  
  <pre><code>2014-08-14 10:44:39:663 Identify[5718:60b] Saving to context(&lt;NSManagedObjectContext: 0x145b82c0&gt;)
  2014-08-14 10:44:39:666 Identify[5718:60b] FRC calling beginUpdates in controllerWillChangeContent
  2014-08-14 10:44:39:666 Identify[5718:60b] FRC done calling beginUpdates in controllerWillChangeContent
  2014-08-14 10:44:39:668 Identify[5718:60b] FRC updated object
  **2014-08-14 10:44:39:671 Identify[5718:60b] FRC calling endUpdates in controllerDidChangeContent
  **2014-08-14 10:44:40:889 Identify[5718:60b] FRC done calling endUpdates in controllerDidChangeContent
  2014-08-14 10:44:41:018 Identify[5718:60b] Time to save in context(&lt;NSManagedObjectContext: 0x145b82c0&gt;): 1.355229
  </code></pre>