All posts

How Does Core Data Save?

Originally posted on Tumblr, 1 November 2013.

I’ve used Core Data in nearly all of the projects I have worked on in the last 8 years or so, but some aspects of the framework still remain a bit of a mystery to me. For example, the exact sequence of events that take place during a save operation. I’ve always known how it works in general terms — enough for someone developing consumer apps — but never in any great detail.

That’s changed recently due to Ensembles, my new open source, Core Data syncing framework. I have had to dig into aspects of Core Data that I had previously taken for granted. How data is saved is pretty important to the framework, because it has to capture those exact changes as deltas, for transfer to other devices, where they can be replayed. Get this wrong, and the devices will end up out of sync.

So how does Core Data save? Well, I’m going to state the stages explicitly here, partially so others get a clearer picture, and partially so I can remind myself later how it works.

You call save: on an NSManagedObjectContext The willSave method of the changed NSManagedObject instances is invoked. NSManagedObjectContextWillSaveNotification is posted. At this point, you still have access to the already committed data values, as well as the updated values. You use properties on the context (eg updatedObjects) to find the changed objects, and you can see which values are changed using the changedValues method of NSManagedObject. The committed values (ie those in the store already), are accessible via committedValuesForKeys:. It is important to realize that the changed values here may not be final. Validation and merging can alter what ends up in the store. Validation occurs here. This applies the rules in your model, but also any custom key-value validation methods in your model classes. These validation methods are able to update the data value. If this happens, validation will run again, until it completely passes, or fails. Data is merged with data in the NSPersistentStoreCoordinator and the NSPersistentStore. The winning data is chosen according to the mergePolicy you have set on your NSManagedObjectContext. This phase can also alter the data values, depending on your merge policy. If data is changed by merging, the data is again validated. At this point, merging and validation are complete, and the data is committed to the on-disk store. The NSManagedObjectContextDidSaveNotification is fired. You no longer have access to pre-save committed values here, so you would need to save those in the NSManagedObjectContextWillSaveNotification method if you need them. You can use the values in the userInfo of the NSNotification to learn which objects were inserted, updated, and deleted, but you can’t determine what properties were changed. You would need to store data in the will-save method if you need that. Also consider that you cannot guarantee which observer gets this notification first, which may be relevant if some observers are changing the data post-save. If that is the case, using the committedValuesForKeys: method may be the best way to ensure you have the data that was actually saved to disk.

From the perspective of developing Ensembles, the most important lessons for me are, first, that the changed values at the time of the will-save notification are not final, and can differ from what ends up in the store. Both validation and merging can alter these values. And second, that object values post save could potentially be changed by other observers, and it is important to use the committed values to access what was saved.

What About Child Contexts?

There is one final aspect of Core Data saving that I have never been totally sure about: How child contexts handle saving.

For example, when an NSManagedObjectContext saves to a store shared with other contexts, you generally merge save changes using the mergeChangesFromContextDidSaveNotification: method upon observing the did-save notification. But whether you need to merge changes into a parent context when a child context saves is not clear from Apple’s docs. It can’t hurt to merge the notification, but is it actually necessary? As it turns out, it isn’t necessary. Saving a child immediately updates the values in the parent context, without an explicit merge.

It is also noteworthy that merge policies don’t seem to have any impact when saving a child context. The values in the child overwrite the parent’s in-memory values directly. Changes made in the parent context are discarded if there are corresponding modifications in the child context. The child takes precedence when it saves.