I'd suggest using multiple contexts more, not less, in applications.
I'll generally use a shared "Library" instance to mediate access to data in a shoebox-style (as opposed to document-style) app. The Library has a root context with private queue concurrency, and has methods used by other parts of the app to obtain new child contexts: One to create a new main-queue child context, and one to create a new background (private-queue) context. The root context is never directly vended, and neither are objects from it; it's primarily used to save changes propagated from the child contexts, and the Library accomplishes this by observing the did-save notifications for child contexts it creates. When it saves, it also posts its own notification (wrapping the root context's did-save notification) so any longer-lived contexts can merge changes upwards from the root.
So in some piece of UI, such as a window controller on OS X or a view controller on either OS X or iOS, I'll ask the Library for a new main-queue context and use that to present that UI—and only that UI. If I need to perform some calculations or a background update (say from a web service), I'll ask the Library for a new background context and use it for just that operation. Any save in either case triggers a subsequent save by the Library's root context, and a notification of that which can be used to update other child contexts.
And any time I'm passing objects around, I use managed object IDs rather than the objects themselves, so there's no concern about mixing objects from different contexts.
With the above in mind, it winds up being pretty straightforward to build nicely-performing apps that use Core Data to persist their object graphs.
Um where you're describing context tree you say that root MOC has main queue concurrency and then that main queue has private concurrency, which is wrong. The picture is right
That said I agree on conclusion, I started my first (quite big) core data app constantly worrying about performance, so I juggled all kinds of MOCs around, but then I just threw all of them out, start with a single main queue MOC and only add private queues where absolutely needed.
I honestly hated Core Data but then I found MagicalRecord (https://github.com/magicalpanda/MagicalRecord) and it solved all my problems. Given that at that point it takes less than 10 minutes to get functioning, Core Data no longer is a pain in the ass.
Looks like someone missed the point of making a whole API on top of the database - to make accessing the database easier, not infinitely harder with more gotchas than Apple's TOS. This reads less like a defense of Core Data and more like a guide book on why using Core Data is terrible idea.
OP here... accessing the underlying SQLite database is very easy by using Core Data. Compare it with using the SQLite C API directly for one second: As things grow you will have more or less the same "problems" with the plain C API when it comes to concurrency. Have a look at FMDB (a popular SQLite wrapper written in Objective-C). You will see a class called FMDatabaseQueue which tries to simplify concurrency with SQLite. It follows the same "pattern" as concurrency typed contexts.
So I argue that you have the exact same problems when using Core Data that you have when using the raw C API for SQLite - conceptually. The benefit of using Core Data is a nicer API and other features that you would have to reimplement...
> So I argue that you have the exact same problems when using Core Data that you have when using the raw C API for SQLite - conceptually.
That's pretty much the same conclusion I've come to from using CoreData, but I see that as a reason not to use CoreData. If I'm going to depend on a very complex closed-source library, I want it to solve hard problems, not just the easy problems. IMO CD isn't vastly harder than doing the same thing manually as some people will claim, but it's also not easier in nontrivial applications.
Despite it's flaws, I have found Core Data to be quite necessary when working with iOS database driven apps, as it provides some rather crucial functionality that would otherwise be lost (or need to be manually recreated) when working with raw SQLite. Concurrency support (as mentioned) is one such example.
Another important function is the support it provides for object change tracking. As your objects are modified, Core Data will dispatch the relevant notifications so you can keep your views updated with the latest changes. If your objects can be updated from multiple places (say, multiple areas of your UI, plus changes occurring in a background thread if you're importing in bulk, or syncing data to a web service), you'll need some way to react to these updates efficiently. This is something that Core Data provides out of the box that you would otherwise have to handle manually, and refreshing the UI in real-time to database changes is something that is mandatory in any modern app. NSFetchedResultsController takes this one step further by determining which changes are relevant to your table view or collection view and inserting/removing/updating the correct cells as necessary. And it does so efficiently which is incredibly relevant on mobile devices. (Note that ContentProviders on Android have similar capabilities for notifying you when a particular object is inserted/updated/deleted. I prefer the Core Data approach as it actually tracks specifically what has changed.)
That said, Core Data is not without it's flaws. I find it somewhat overengineered in some areas and rather lacking in others. For example, the 'abstraction' of Core Data backends is a weakness rather than a strength. In practice, I would wager that 99% of people using Core Data are using it with the SQLite backend. Having to cater to the existence of an alternate XML backend means that certain actions can't assume (and hence take advantage of) SQLite specific features. For example:
- The inability to issue bulk updates or queries. Instead of doing a DELETE FROM x WHERE y, you have to iterate through all the objects you want to delete or update. This can be slow.
- If you need to do anything custom in a migration, it would be great to be able to write a SQL level migration script instead of going through the extremely cumbersome manual migration process.
Additionally, the recently added concurrency options are still missing support for a very common use case: running your initial fetch on a background thread, and then populating a table view with the objects you just fetched. This is technically possible to do but not very easy, as you can't safely pass managed objects between thread boundaries without a bunch of extra work, and not supported by the native NSFetchedResultsController so you lose the ability to use that. (Again, to compare to Android, their loader framework makes doing background queries very easy to implement.)
Overall I think Core Data is a huge plus for iOS/Mac developers but there is definitely lots of room for improvement.
I'll generally use a shared "Library" instance to mediate access to data in a shoebox-style (as opposed to document-style) app. The Library has a root context with private queue concurrency, and has methods used by other parts of the app to obtain new child contexts: One to create a new main-queue child context, and one to create a new background (private-queue) context. The root context is never directly vended, and neither are objects from it; it's primarily used to save changes propagated from the child contexts, and the Library accomplishes this by observing the did-save notifications for child contexts it creates. When it saves, it also posts its own notification (wrapping the root context's did-save notification) so any longer-lived contexts can merge changes upwards from the root.
So in some piece of UI, such as a window controller on OS X or a view controller on either OS X or iOS, I'll ask the Library for a new main-queue context and use that to present that UI—and only that UI. If I need to perform some calculations or a background update (say from a web service), I'll ask the Library for a new background context and use it for just that operation. Any save in either case triggers a subsequent save by the Library's root context, and a notification of that which can be used to update other child contexts.
And any time I'm passing objects around, I use managed object IDs rather than the objects themselves, so there's no concern about mixing objects from different contexts.
With the above in mind, it winds up being pretty straightforward to build nicely-performing apps that use Core Data to persist their object graphs.