BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News Three Ways to Get Core Data Multithreading Right

Three Ways to Get Core Data Multithreading Right

At #Pragma Conference 2015, Marcus Zarra, author of books about Core Data and Core Animation, described three approaches to using Core Data in a multithreaded environment and tried to clear up how Core Data should be used in 2015. Indeed, Zarra says, one of the problems you face when working with an eleven year old technology such as Core Data is that a lot of information is available, but it is not easy to find out which piece of information is still accurate and which isn’t.

Multithreading should be used, according to Zarra, when we know that we have some spare CPU so we can process data ahead of time that users are going to use next. Another important use case for multithreading is to improve responsiveness of an app by allowing users not having to wait for a lengthy operation to finish, e.g. a network operation. Multithreading is almost never a solution to a performance problem and it is a fundamental design decision, not an afterthought.

The original way

The original way was the only one available before iOS 6. It still works today, though Zarra suggests against using it except in certain edge cases. It is based on four main principles:

  • One NSPersistentStoreCoordinator (PSC) handles all the interactions to disk.
  • NSManagedObjectContexts (MOCs) talk to the PSC and do not know anything about each other.
  • One of the MOCs is in charge of updating the UI and play the role of a single source of truth.
  • The only way for a MOC to become aware of changes in another MOC is handling an NSNotification by merging.

This design has a few shortbacks, such as a lot of boiler-plate code that needs to be written, unclearness of threading rules leading to sporadic crashes, and unexpected thread blocking. This has improved a bit with iOS 8 and Yosemite thanks to a debug flag that will make an app crash if it violated Core Data concurrency model.

The hard way

What Zarra calls the hard way is an approach that relies on SQLite being designed for multi-process access. This means that we can have multiple PSCs, with each MOC having its own PSC. This will have the nice effect of getting rid of any locking issues and enabling almost full asynchronous access – provided you don’t write the same table and row from two PSCs at the same time.

Even with this design, it is desirable to have just one MOC that feeds data to the UI. What this approach makes hard is synchronizing data across PSCs, since they do not know anything about each other. Furthermore, threading and maintainability are also impaired. The interesting side of this approach lies with the fact that this is exactly how iCloud works.

The best way

The best way, according to Zarra, is not the fastest, but it is by far the easiest and most maintainable. It relies on new APIs that Apple introduced with iOS 6, which allows to define child MOCs and also specify a MOC’s concurrency type. The design that Zarra presents is based on how NSManagedDocument works and uses:

  • A single persistent store coordinator.
  • A private MOC which is the only one that actually accesses the PSC.
  • A main MOC associated to the UI that is a child of the private MOC.
  • Multiple child MOCs that are specific to secondary threads.

The nice thing about this design is that all changes in a child MOC will automatically propagate to its parent MOC, thus doing away with the need for merging.

The main drawback of this design is its slowness, although only by a few percents, says Zarra. A tricky issue with it is that if too many async operations are carried through, there may be ripple effects on the UI, since its associated MOC will receive multiple changes in sequence that might not be coherent with one another.

An important detail about this design is that it is best not to reuse child MOCs, which are cheap to create. On the other hand, long-lived child MOCs should be manually kept in sync with the main MOC, since changes are propagated only from child MOCs to their parent and not the other way round.

A final remark by Zarra is that using NSFetchedResultsController will block the UI, so you better be prepared to it.

Rate this Article

Adoption
Style

BT