Core Data is a framework Apple provides to developers that is described as a "schema-driven object graph management and persistence framework."
The framework manages where data is stored, how it is stored, data caching, and memory management. It was ported to the iPhone from Mac OS X with the 3.0 iPhone SDK release.
The Core Data API allows developers to create and use a relational database, perform record validation, and perform queries using SQL-less conditions. It essentially allows you to interact with SQLite in Objective-C and not have to worry about connections or managing the database schema, and most of these features are familiar to people who have used object-relational mapping (ORM) technologies like those implemented in Ruby on Rails, CakePHP, LINQ or other libraries and frameworks that abstract access to the database. The main benefit to this approach is that it eliminates the development time otherwise necessary to write complex SQL queries and manually handle the SQL and output of those operations.
Core Data is the model layer of your application in the broadest sense possible. It's the Model in the Model-View-Controller pattern permeates the iOS SDK.
Core Data isn't the database of your application nor is it an API for persisting data to a database. Core Data is a framework that manages an object graph. It's as simple as that. Core Data can persist that object graph by writing it to disk, but that is not the primary goal of the framework.
Core Data Stack
The Core Data stack is the heart of Core Data. It's a collection of objects that make Core Data tick. The key objects of the stack are the managed object model, the persistent store coordinator, and one or more managed object contexts.
The managed object model is the data model of the application. Even though Core Data isn't a database, you can compare the managed object model to the schema of a database, that is, it contains information about the models or entities of the object graph, what attributes they have, and how they relate to one another.
The NSManagedObjectModel object knows about the data model by loading one or more data model files during its initialization.
The model contains one or more NSEntityDescription objects representing the entities in the schema. Each NSEntityDescription object has property description objects (instances of subclasses of NSPropertyDescription) that represent the properties (or fields) of the entity in the schema. The Core Data framework uses this description in several ways:
- Constraining UI creation in Interface Builder
- Validating attribute and relationship values at runtime
- The mapping between your managed objects and a database or file-based schema for object persistence.
A managed object model maintains a mapping between each of its entity objects and a corresponding managed object class for use with the persistent storage mechanisms in the Core Data Framework. You can determine the entity for a particular managed object with the entity method.
The NSPersistentStoreCoordinator object persists data to disk and ensures the persistent store(s) and the data model are compatible. It mediates between the persistent store(s) and the managed object context(s) and also takes care of loading and caching data.
The persistent store coordinator is the conductor of the Core Data orchestra. Despite its important role in the Core Data stack, we rarely interact with it directly.
Coordinators do the opposite of providing for concurrency—they serialize operations. If you want to use multiple threads for different write operations you use multiple coordinators. Note that if multiple threads work directly with a coordinator, they need to lock and unlock it explicitly. Each coordinator (and thus container) may use different copies, and hence different versions, of a managed object model. This allows you to cleanly deal with file versioning.
The coordinator gives access to its underlying object stores. You can retrieve an object store when you first add one (using addPersistentStoreWithType:configuration:URL:options:error:), or by using persistentStoreForURL: or persistentStores. This allows you to determine, for example, whether a store has already been added, or whether two objects come from the same store:
- You move a store from one location to another or change the type of a store, using migratePersistentStore:toURL: options:withType:error:
- You can set metadata for a given store using the persistent store coordinator (setMetadata:forPersistentStore:)
The NSManagedObjectContext object manages a collection of model objects, instances of the NSManagedObject class. It's perfectly possible to have multiple managed object contexts. Each managed object context is backed by a persistent store coordinator.
You can see a managed object context as a workbench on which you work with your model objects. You load them, you manipulate them, and save them on that workbench. Loading and saving are mediated by the persistent store coordinator. You can have multiple workbenches, which is useful if your application is multithreaded.
While a managed object model and persistent store coordinator can be shared across threads, managed object contexts should never be accessed from a thread different than the one they were created on.
If a context’s parent store is another managed object context, fetch and save operations are mediated by the parent context instead of a coordinator. This pattern has a number of usage scenarios, including:
- Performing background operations on a second thread or queue.
- Managing discardable edits, such as in an inspector window or view.
As the first scenario implies, a parent context can service requests from children on different threads. You cannot, therefore, use parent contexts created with the thread confinement type.
When you save changes in a context, the changes are only committed “one store up.” If you save a child context, changes are pushed to its parent. Changes are not saved to the persistent store until the root context is saved. (A root managed object context is one whose parent context is nil.) In addition, a parent does not pull changes from children before it saves. You must save a child context if you want ultimately to commit the changes.