In defense of Core Data (Part I)
thermal-core.com
thermal-core.com
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.
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.
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...
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.