Multithreading with Core Data on iOS

Silk Shop by anatamanA while ago I recommended not to multithread at all with Core Data. That was true during Snow Leopard’s days. But with the advent of context hierarchy and the pervasive use of blocks on Lion and iOS 5, multi-threading with Core Data becomes approachable.

As I was working on Resonate, I discovered a number of lessons on writing multithreaded applications on iOS. A lot of these lessons were discovered “the hard way” – through misguided attempts and many crashes. I’d thought I share this with you so that you don’t need to go through the same mistakes.

Even though I use the word “multithreading”, chances are you won’t need to deal with threads directly. Using operation queues is the way to go for most of your multiprocessing mechanisms. Simply package your algorithms into distinct units of work and then let NSOperationQueue manage them for you. Don’t create NSThread instances directly unless you have a pretty damn good reason why.

Use UIManagedDocument for Core Data apps on iOS.

Really, when you’re writing iOS 5 applications that uses Core Data, you’ll want to use UIManagedDocument. At a bare minimum, use this class to manage your Core Data stack. That is, let core data manage your instances of NSManagedObjectContext, NSPersistentStoreCoordinator, and other parts of the Core Data stack.

Even if you need to share your Core Data classes with your Mac app, you can still separate your entity classes that will work accross both platforms. If you think that UIManagedDocument isn’t good enough for your app, you’d better have a damn good reason why.

When you use UIManagedDocument, you’ll get a set of interrelated NSManagedObjectContext instances. A main context that you use as per normal with your GUI objects and the root context, which is the main context’s parent that are used for asynchronously saving data to persistent storage. When dealing with object contexts created by UIManagedDocument you should not save them directly – you’ll bypass undo management and a lot of other behind-the-scenes stuff. UIManagedDocument will periodically save these for you in the correct order.

Use the managed object context hierarchy

From this building block, I go further to recommend a pattern for using the new context hierarchy. I discovered this works nicely on iOS when I was working on Resonate.

  • Use the root context for saving data obtained by network refreshes.
  • Use the main context for GUI components and others that needs to be in the main thread.
  • Create individual worker contexts as children of the main context for background data processing tasks.

Core data context use

Use the Root Context for Network Refreshes

Being a Twitter client, Resonate often need to ask Twitter for the latest tweets and other data. When Twitter returns and Resonate parsed the resulting JSON data, it then invokes the root context’s operation queue to store the parsed JSON data into managed objects.

You can use this pattern yourself. You run network operations asynchronously, parse the resulting data, and then when you’re ready to store it you call performBlock on the root context.

You can see the pattern in the sequence diagram below. Typically the user initiates the refresh from an action in the view controller. If you use ASIHTTPRequest for your network I/O, you can make it run asynchronously. When the request is complete, you pass the raw data into an operation queue for parsing (note that +[ASIHTTPRequest sharedQueue] is convenient for this). When the data is parsed, you make use of the root context’s private queue to update the data to your managed objects. Upon completion of the update, you pass the object IDs of the updated objects back to your view controller in the main thread. Please take care not to pass the actual managed object instances between operation queues as this will certainly cause problems.

Network Refresh Pattern

One caveat of this approach is that you need to separate network-sourced data with user-edited data. That is attributes that can be updated from the network should not be editable by the user. This is to prevent conflicts when UIManagedDocument tries to saves your contexts.

Use Multiple Child Contexts for Worker Queues

If you need to do some long-running data processing, you should run those in a background operation queue and not in the main thread. But really to make your app responsive, anything that may take longer than half a second should be taken off the main thread – especially important in iOS devices where the CPU is a lot slower than the one found in OS X machines.

Those background operations should use their own private NSManagedObjectContext instances. I also recommend that these contexts are set as children of the main context. Why? Because when the operation completes and the context is saved then the results are “pushed” into the main context. This also rescues the GUI from having to refresh its NSManagedObjectContext to get the updated objects. Since you create these contexts yourself, you are responsible for saving them – unlike the main and root contexts that are owned by UIManagedDocument.

How to use it? Refer to the interaction diagram below. Typically the view controller creates the worker operation class (which is an instance of NSOperation). As part of the worker’s initial setup regime, the view controller provides its context as the parent context for use by the worker object. Then when the worker starts it creates its own private NSManagedObjectContext instance and use that context as it’s scratchpad data store. Just before the worker completes, it saves the context to push its changes to the main thread’s context.

Core Data for Background Processing

Conclusion

You now know how to multi-thread effectively in Core Data applications. Although this article focuses on iOS, you should be able to apply the same principles to OS X. You have learned about:

  • Using UIManagedDocument for managing your Core Data stack
  • The new NSManagedObjectContext hierarchy (nested contexts)
  • How use each level in the context hierarchy for different operation types (network refresh, main/GUI thread, worker operation).

Until next time, bye now.

 



Avoid App Review rules by distributing outside the Mac App Store!


Get my FREE cheat sheets to help you distribute real macOS applications directly to power users.

* indicates required

When you subscribe you’ll also get programming tips, business advices, and career rants from the trenches about twice a month. I respect your e-mail privacy.

Avoid Delays and Rejections when Submitting Your App to The Store!


Follow my FREE cheat sheets to design, develop, or even amend your app to deserve its virtual shelf space in the App Store.

* indicates required

When you subscribe you’ll also get programming tips, business advices, and career rants from the trenches about twice a month. I respect your e-mail privacy.

2 thoughts on “Multithreading with Core Data on iOS

Leave a Reply