0
votes

In magicalRecord [on iOS 7 application] there are two NSManagedObjectContexts:

  1. RootSavingContext of type NSPrivateQueueConcurrencyType
  2. DefaultContext of type NSMainQueueConcurrencyType

While doing a very heavy database [.sqlite] insert/update/delete how one can make use of above two contexts such that database save [final disk I/O] too happens in background with no use of main thread. Right now the code in MagicalRecord

+ (void)rootContextChanged:(NSNotification *)notification {
if ([NSThread isMainThread] == NO) {
    dispatch_async(dispatch_get_main_queue(), ^{
        [self rootContextChanged:notification];
    });

    return;
}

[[self MR_defaultContext] mergeChangesFromContextDidSaveNotification:notification];

}

Is using main thread to save data to database. Instead isn't it possible that the entire thing do takes place in a background with no intervention of main thread at all. Though MagicalRecord exposes methods like MR_Context for creating background NSManagedObjectContext but doing a save operations comes on main thread. I am creating background context in GCD and then uses performBlockAndWait like this:

if ([bContext hasChanges]) {
            NSError* __autoreleasing error;
            [bContext save:&error];
            [bContext.parentContext saveToPersistentStoreAndWait];
        }
1

1 Answers

0
votes

I can't speak as to Magical Record as I've never used it.

But as to possibility, assuming you've used a SQLite store there's a bit of a problem: SQLite doesn't allow simultaneous multithreaded access. Per its developers "Threads are evil. Avoid them."

In Core Data terms what that often means is that you instantiate a save from a background context and the SQL INSERTs, DELETEs, etc all occur in the background, the write to disk occurs in the background, etc, but while that happens all access to the persistent store is locked. If the main queue tries to run a query, or even just fault in an object, then it has to block, waiting for the save to complete.

So in at least one very real sense, you can't guarantee that there'll be no impact on the main thread unless you can guarantee that the main thread won't try to access the store at all.

Common solutions include using the dictionary versions of managed objects on the main queue with explicit SQL-style fetches, not necessarily on the main queue since immutable dictionaries are thread safe, using some other custom structure that is built from Core Data but is subsequently unlikely to refer to it, just not worrying about it if the background saves are relatively short and it's unlikely you'll hit one, or even breaking up the saves in the background into small chunks to decrease the maximum amount of time the main queue may block. Be careful with the final one though, as insertion costs raise non-linearly in SQLite so e.g. inserting five lots of ten objects takes longer in total than inserting fifty objects.

I've also managed to get some good mileage out of grabbing anything I think the user may want in the near future on the main thread when the background announces that it plans to save shortly.