Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple NSManagedObjectContexts or single context and -performBlock

I have been using Core Data with a single NSManagedObjectContext for a long time, all fetching, saving, background update operations will be done on single context through helper classes, I was planning to implement a multiple NSManagedObjectContext approach (which is the recommended solution in most of my searching).

My question is: is performBlock the only was to execute code for that context? Can't we do something like below:

- (void) checkSyncServer {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        //do something here, check and fetch data
        // create NSManagedObject's 
        [_tempContext save:&error];  
        //masterContext will merge changes through notification observers
    });
}

(i.e) execute code apart from -performBlock method. How can I execute multiple asynchronous methods and perform a save?

However, I find a single context (which is managed by one singleton NSObject class) simpler to use.

This multiple context with ContextConcurrencyType looks more complicated (in terms of execution flow). Is there a better solution?

like image 927
Ramesh Lingappa Avatar asked Mar 20 '14 16:03

Ramesh Lingappa


People also ask

Can we create multiple managed object context?

Most apps need just a single managed object context. The default configuration in most Core Data apps is a single managed object context associated with the main queue. Multiple managed object contexts make your apps harder to debug; it's not something you'd use in every app, in every situation.

What is NSManagedObjectContext?

An object space to manipulate and track changes to managed objects.

What is Persistentcontainer in Swift?

NSPersistentContainer simplifies the creation and management of the Core Data stack by handling the creation of the managed object model ( NSManagedObjectModel ), persistent store coordinator ( NSPersistentStoreCoordinator ), and the managed object context ( NSManagedObjectContext ).

What is NSManagedObjectModel in Swift?

NSManagedObjectModel represents your application model file describing your app's types, properties, and relationships. NSManagedObjectContext tracks changes to instances of your application types. NSPersistentStoreCoordinator used to saves and fetch instances of your application types from stores.


1 Answers

You can access contexts in one of two ways:

  • On its Thread/Queue. This applies to confined contexts and main queue contexts. You can access them freely from their own thread.
  • With -performBlock: if it is a private queue context or if you are touching the context from a thread other than the one it belongs on.

You cannot use dispatch_async to access a context. If you want the action to be asynchronous then you need to use -performBlock:.

If you were using a single context before and you were touching it with a dispatch_async you were violating the thread confinement rule.

Update

When you call [[NSManagedObjectContext alloc] init] that is functionally equivalent to [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType].

The NSManagedObjectContext has always been thread confined.

As for executing multiple methods you can just call them all in the same block:

NSManagedObjectContext *moc = ...;
[moc performBlock:^{
  //Fetch something
  //Process data
  //Save
}];

Or you could nest them if you wanted them to be all async of each other:

NSManagedObjectContext *moc = ...;
[moc performBlock:^{
  //Fetch Something
  [moc performBlock:^{
    //Process Data
  }];
  [moc performBlock:^{
    //Save
  }];
}];

Since -performBlock: is re-entrant safe you can nest them all you want.

Update Async save

To do an async save you should have two contexts (or more):

  • Main Queue context that the UI talks to
  • Private Queue context that saves

Private context has a NSPersistentStoreCoordinator and the main queue context has the private as its parent.

All work is done in the main queue context and you can save it safely, normally on the main thread. That save will be instantaneous. Afterwards, you do an async save:

NSManagedObjectContext *privateMOC = ...;
NSManagedObjectContext *mainMOC = ...;

//Do something on the mainMOC

NSError *error = nil;
if (![mainMOC save:&error]) {
  NSLog(@"Main MOC save failed: %@\n%@", [error localizedDescription], [error userInfo]);
  abort();
}

[privateMOC performBlock:^{
  NSError *error = nil;
  if (![privateMOC save:&error]) {
    NSLog(@"Private moc failed to write to disk: %@\n%@", [error localizedDescription], [error userInfo]);
    abort();
  }
}];

If you already have an app, all you need to do is:

  • Create your private moc
  • Set it as the parent of your main
  • Change your main's init
  • Add the private block save method whenever you call save on your main

You can refactor from there but that is all you really need to change.

like image 62
Marcus S. Zarra Avatar answered Nov 09 '22 08:11

Marcus S. Zarra