Running with Scissors

Both of these APIs work by making changes directly on disk. When we use either of these APIs, Core Data will construct the appropriate SQL calls and then pass them to SQLite. Nothing gets loaded into memory and therefore the API is executed very quickly—just slightly slower than SQLite itself.

If we can just make changes and/or deletes on disk and avoid having to load them all into memory, why don’t we just do that all the time?

This API comes at a fairly significant cost. The changes that we make on disk aren’t passed to the NSManagedObjectContext instances in our application.

This means that we can very easily make a change to the data on disk and then our NSManagedObjectContext will try to make a different change and cause issues. When the first API was introduced, Apple likened these APIs to running with scissors. You can do it, but the risk is greater.

First, data validation is performed in memory. When we make a change directly on disk we’re bypassing the validation steps that Core Data normally performs. This means we can break a relationship and have dangling references, we can inject data that’s not valid, and so forth. Worse, our application won’t notice the issue until it attempts to load the data later and then the user is left in a bad state.

Second, when the changes are made on disk, the version number of the object is updated (as it should be). However, since nothing in memory knows of this change, the version number in memory won’t match. If Core Data were to attempt to do a save of an object in this state, a merge conflict would result with a potentially negative outcome.

And of course there’s the obvious issue: our user interface won’t know about the change and therefore the older data will still be displayed.

We can address these issues, but doing so requires more code on our part. Let’s start by looking at a bulk update.