fbpx

Blogs from the Ranch

< Back to Our Blog

Introducing the Open-Source Big Nerd Ranch Core Data Stack

Avatar

Robert Edwards

When we at the Ranch use Core Data, we inevitably end up using it in a multi-threaded environment. The Right Way™ to use Core Data across multiple threads is a topic of fierce debate, one that we had ourselves when we set out to create a shared Core Data stack.

And now we want to share the Core Data stack we created with you. The Big Nerd Ranch Core Data Stack was designed to effortlessly fit the needs of most users, while providing the flexibility to accommodate more esoteric needs.

Stack Design Patterns

To best describe the design choices made in the Big Nerd Ranch Core Data stack, we’ll first walk through the various patterns of multi-threaded Core Data stacks.

Multi-threaded Core Data stack legend

Shared Persistent Store Coordinator Pattern

The first pattern is constructed with a single persistent store coordinator, connected to a main queue managed object context. Background worker managed object contexts are created as needed and share the same single instance of the persistent store coordinator.

Saving any one of these managed object contexts will push changes through the shared coordinator and on to the backing store file. While the changes from each context are available in the store after a save, no context is aware of the changes made in any other context.

In order to keep all contexts in sync, the stack must subscribe to NSManagedObjectContextDidSaveNotification notifications and then call mergeChangesFromContextDidSaveNotification() on the remaining contexts.

Shared Persistent Store Coordinator Pattern

Pros

  • Worker contexts are kept in sync with each other
  • Can be more performant with large data sets. More to come on this later in the post.

Cons

  • Greater complexity with setup to ensure consistency across contexts
  • Increased chance of conflicting changes at the store level since they share a single coordinator
  • Persistent store coordinator spends more time locking the store, affecting performance

Nested Managed Object Context Pattern

In iOS 5.0 / OS X 10.7, Apple introduced the concept of nested managed object context through the property parentContext. Fetch requests performed on a context will work their way up through the context’s parentContexts until it reaches an NSPersistentStore where the objects are retrieved. Performing a save operation on a context will push the changes up a single level.

Nested Managed Object Context Pattern

Pros

  • Much simpler setup compared to Shared Persistent Store Coordinator Pattern
  • Children contexts always have most recent changes from parents available
  • Minimizes contexts interfacing with the coordinator to a single context
  • Writes to the store performed on a background queue

Cons

  • Slower than Shared Persistent Store Coordinator Pattern when inserting large data sets
  • awakeFromInsert being called on NSManagedObject subclasses for each context in the parent chain
  • Merge policies only apply to a context saving to a store and not to its parent context

Shared Store Stack Pattern

One criticism of the nested managed object context pattern is that inserting a large number of objects (on the order of thousands) is significantly slower than doing the same operation with a shared persistent store coordinator stack. See Concurrent Core Data Stack Performance Shootout. For this reason some have outright avoided using a nested managed object context pattern in favor of the shared persistent store coordinator pattern.

Apple, however, recommends an alternate approach for solving this type of performance issue, saying:

If you’ve exhausted all other optimizations and are still seeing performance issues, you might want to consider using a different concurrency style, and this time you have two persistent store coordinators, two almost completely separate Core Data stacks. One stack for your background work, one stack for your main queue work and they both talk to the same persistent store file.

(via WWDC 2013 Session 211 – Core Data Performance Optimization and Debugging. Transcript.)

Shared Store Stack Type

This setup allows the two stacks to work in parallel with the exception of locking the store file, which is a very fast operation.

The changes could be propagated across contexts using the same pattern described in shared persistent store coordinator pattern using mergeChangesFromContextDidSaveNotification(). However, since this pattern is built for performance, it’s preferable to simply listen for the NSManagedObjectContextDidSaveNotification and re-perform your fetch request on the main queue context stack. This will be considerably faster than trying to merge thousands of objects between the two stacks.

Pros

  • Much faster when inserting large data sets

Cons

  • Heavy-weight setup when creating the background context because of the need to create an entire stack
  • More complex setup compared to nested managed object context pattern
  • Syncing changes between stacks is manual

The Big Nerd Ranch Core Data Stack

This brings us to the Big Nerd Ranch Core Data Stack. For us, the simplicity of the nested managed object context pattern greatly outweighs any performance deficiencies.

At the root of our stack is a PrivateQueueConcurrencyType managed object context that handles writing to our store. Because this context is on a private queue, we benefit from moving disk writing off the main queue. Interacting with managed objects themselves should occur through one of this context’s children.

The root context has one child context, which is a MainQueueConcurrencyType context. This context should be used for any main queue or UI related tasks. Examples include setting up an NSFetchedResultsController, performing quick fetches, making UI related updates like a bookmark or favoriting an object.

For any longer running task, such as inserting or updating data from a web service, you should use a new background worker context. The stack will vend you one via the newBackgroundWorkerMOC() function.

Since saving an NSManagedObjectContext will only propagate changes up a single level to the parentContext, the Big Nerd Ranch Core Data Stack listens for save notifications and ensures that the changes get persisted all the way up the chain to your store.

You may sometimes need to perform large import operations where the nested context performance would be the bottleneck. For that, we’ve included a function to vend you a managed object context with its own stack newBatchOperationContext(setupCallback: CoreDataStackBatchMOCCallback). This follows the pattern outlined in Shared Store Stack Pattern.

The Big Nerd Ranch Core Data Stack

Asynchronous Setup

The Big Nerd Ranch Core Data stack can be constructed with either an NSSQLiteStoreType store or an NSInMemoryStoreType store. Since a SQLite backed store could potentially need to perform model migrations and take an indefinite amount of time, the stack construction is asynchronous. Similarly, creating a new batch operation context will also be asynchronous, since it relies on constructing a discrete stack of its own.

For usage details, see Usage – Standard SQLite Backed.

Large Import Operation Context

In most cases, offloading your longer-running work to a background worker context will be sufficient to alleviate performance woes. If you find yourself inserting or updating thousands of objects, then perhaps the shared store pattern is a good fit for your use case. The following WWDC videos are helpful in ensuring you’ve done your due diligence in optimizing your fetches, inserts and queries before opting to go this route:

For usage details, see Usage – Large Import Operation Context.

Our Core Data Stack and Your Projects

We’re happy to say we’ve had success incorporating this stack on multiple client projects. We hope it brings success to your project.

Avatar

Robert Edwards

Not Happy with Your Current App, or Digital Product?

Submit your event

Let's Discuss Your Project

Let's Discuss Your Project