Core Data addresses two main areas of functionality: object graph management, and object persistence. Object graph management includes undo and redo, validation, and maintaining the integrity of relationships between objects. Object persistence means saving objects to and fetching objects from a persistent store (such as a file on disk). Typically, you are responsible for writing all the code to support this functionality. The Core Data Framework provides an infrastructure to manage these tasks for you.
Introduction
Object Graphs
Basic Core Data Architecture
Managed Objects and the Managed Object Model
What Core Data Is Not
Core Data provides an infrastructure for object graph management and persistence. This article first describes what is an object graph and introduces some basic terminology. It then describes the basic Core Data architecture, and the way you use the framework. In order both to manage the object graph and to support object persistence, Core Data needs a rich description of the objects it operates on. You provide this description by creating a "managed object model"—a schema that describes the entities your application uses and the relationships between them. The managed object model is described in the penultimate section, before a description of some things that Core Data is not. For another high-level description of Core Data, see Developing with Core Data.
An object graph is a collection of objects, including the relationships between them. It is important to understand what an object graph is, and in particular how relationships between objects are represented in order to fully appreciate the functionality Core Data provides.
In most applications you face the task of managing a graph of model objects (model in the sense of the Model-View-Controller design pattern—see “The Model-View-Controller Design Pattern” in Cocoa Design Patterns). These represent the data in your application—for example, graphics objects, to-do items, or employee objects. Consider the following example, which will be used throughout the remainder of this document.
In the example, an employee is represented by an Employee object that has a number of properties: attributes representing first name, last name, and salary, and relationships to a manager and to the department in which they work. (To understand the differences between property, attribute, and relationship, see "Object Modeling" in Cocoa Design Patterns.) A department in which employees work is represented by a Department object that has attributes representing a name and budget.
A group of three objects—an employee, the employee’s manager (also an employee), and the department in which the employee works—represents a small object graph, as illustrated in Figure 1. Note that a to-one relationship is represented by a reference to the destination object, and a to-many relationship is represented by a collection object—an NSMutableSet
object—that contains references to the objects representing its members.
In addition to the relationships between an employee and his or her manager and the department, you should also consider the reverse relationships—between a manager and the employees that report to the manager (directReports
) and between department and the employees that work in the department (employees
). In this example these inverse relationships are modeled. It is possible for relationships to be navigable in only one direction (if you are never interested in finding out from a department object what employees are associated with it, then you do not have to model that relationship), however you are strongly encouraged always to model relationships in both directions.
The Core Data framework also defines another kind of relationship (not illustrated here) known as a fetched property. A fetched property is an array calculated by executing a fetch request (see “Fetch Requests”) associated with the source object's entity. Fetched properties allow a weak, unidirectional relationship. An example is an iTunes smart playlist, if expressed as a property of a containing object. Songs don’t “belong” to a particular playlist, and the playlist may remain even after the songs have been deleted or a remote server has become inaccessible. Note, however, that unlike a smart playlist, fetched properties are not dynamically updated. Fetched properties are also useful for modeling cross-store relationships.
Note : This document uses the employees example for reasons of expediency and clarity. It represents a rich but easily understood problem domain. The utility of the Core Data framework, however, is not restricted to database-style applications, nor is there an expectation of client-server behavior. The framework is equally useful as the basis of a vector graphics application such as Sketch or a presentation application such as Keynote.
In most applications, you need a means to open a file containing an archive of objects, and a reference to at least one root object. You also need to be able to archive all the objects to a file and—if you want to support undo—to track changes to the objects.
In an employee management application, you need a means to open a file containing an archive of employee and department objects, and a reference to at least one root object—for example, the array of all employees—as illustrated in Figure 2. You also need to be able to archive to a file all the employees and all the departments.
You are responsible for writing the code that manages these tasks either in whole or in part. The Cocoa document architecture provides an application structure and functionality that helps to reduce the burden, but you still have to write methods to support archiving and unarchiving of data, to keep track of the model objects, and to interact with an undo manager to support undo.
Using the Core Data framework, most of this functionality is provided for you automatically, primarily through an object known as a managed object context (or just context). The managed object context serves as your gateway to an underlying collection of framework objects—collectively known as the persistence stack—that mediate between the objects in your application and external data stores. At the bottom of the stack are persistent object stores, as illustrated in Figure 3.
Note that Core Data is not restricted to document-based applications—indeed it is possible to create a Core Data–based utility with no user interface at all (see Core Data Utility Tutorial). The same principles apply in other applications.
You can think of a managed object context as an intelligent scratch pad. When you fetch objects from a persistent store, you bring temporary copies onto the scratch pad where they form an object graph (or a collection of object graphs). You can then modify those objects however you like. Unless you actually save those changes, however, the persistent store remains unaltered.
Objects that tie into the Core Data framework are known as managed objects. All managed objects must be registered with a managed object context. You add objects to the graph and remove objects from the graph using the context. The context tracks the changes you make, both to individual objects' attributes and to the relationships between objects. By tracking changes, the context is able to provide undo and redo support for you. It also ensures that if you change relationships between objects, the integrity of the object graph is maintained.
If you choose to save the changes you've made, the context ensures that your objects are in a valid state. If they are, then the changes are written to the persistent store (or stores) and new records added for objects you created and records removed for objects you deleted.
You may have more than one managed object context in your application. For every object in a persistent store there may be at most one corresponding managed object associated with a given context (for more details, see “Faulting and Uniquing”). To consider this from a different perspective, a given object in a persistent store may be edited in more than one context simultaneously. Each context, however, has its own managed object that corresponds to the source object, and each managed object may be edited independently. This can lead to inconsistencies during a save—Core Data provides a number of ways to deal with this (see, for example, “Using Managed Objects”).
To retrieve data using a managed object context, you create a fetch request. A fetch request is an object that specifies what data you want, for example, “all Employees,” or “all Employees in the Marketing department ordered by salary, highest to lowest.” A fetch request has three parts. Minimally it must specify the name of an entity (by implication, you can only fetch one type of entity at a time). It may also contain a predicate object that specifies conditions that objects must match and an array of sort descriptor objects that specifies the order in which the objects should appear, as illustrated in Figure 4.
You send a fetch request to a managed object context, which returns the objects that match your request (possibly none) from the data sources associated with its persistent stores. Since all managed objects must be registered with a managed object context, objects returned from a fetch are automatically registered with the context you used for fetching. Recall though that for every object in a persistent store there may be at most one corresponding managed object associated with a given context (see “Faulting and Uniquing”). If a context already contains a managed object for an object returned from a fetch, then the existing managed object is returned in the fetch results.
The framework tries to be as efficient as possible. Core Data is demand driven, so you don't create more objects than you actually need. The graph does not have to represent all the objects in the persistent store. Simply specifying a persistent store does not bring any data objects into the managed object context. When you fetch a subset of the objects from the persistent store, you only get the objects you asked for. If you follow a relationship to an object that hasn't been fetched, it is fetched automatically for you. If you stop using an object (if it's not retained) then it will be deallocated. Note that this is not the same as removing it from the graph.
As noted earlier, the collection of framework objects that mediate between the objects in your application and external data stores is referred to collectively as the persistence stack. At the top of the stack are managed object contexts, at the bottom of the stack are persistent object stores. Between managed object contexts and persistent object stores there is a persistent store coordinator.
In effect, a persistent store coordinator defines a stack. The coordinator is designed to present a façade to the managed object contexts so that a group of persistent stores appears as a single aggregate store. A managed object context can then create an object graph based on the union of all the data stores the coordinator covers. Note that a coordinator can only be associated with one managed object model. If you want to put different entities into different stores, you must partition your model entities by defining configurations within the managed object models (see “Configurations”).
Figure 5 shows an example where employees and departments are stored in one file, and customers and companies in another. When you fetch objects, they are automatically retrieved from the appropriate file, and when you save, they are archived to the appropriate file.
A given persistent object store is associated with a single file or other external data store and is ultimately responsible for mapping between data in that store and corresponding objects in a managed object context. Normally, the only interaction you have with a persistent object store is when you specify the location of a new external data store to be associated with your application (for example, when the user opens or saves a document). Most other interactions with the Core Data framework are through the managed object context.
Your application code—and in particular the application logic associated with managed objects—should not make any assumptions about the persistent store in which data may reside. Core Data provides native support for several file formats. You can choose which to use depending on the needs of your application. If at some stage you decide to choose a different file format, your application architecture remains unchanged. Moreover, if your application is suitably abstracted, then you will be able to take advantage of later enhancements to the framework without any additional effort. For example—even if the initial implementation is able to fetch records only from the local file system—if an application makes no assumptions about where it gets its data from, then if at some later stage support is added for a new type of remote persistent store, it should be able to use this new type with no code revisions.
You can create and configure the persistence stack programmatically. In many cases, however, you simply want to create a document-based application able to read and write files. The NSPersistentDocument
class is a subclass of NSDocument
that is designed to let you easily take advantage of the Core Data framework. By default, an NSPersistentDocument
instance creates its own ready-to-use persistence stack, including a managed object context and a single persistent object store. There is in this case a one-to-one mapping between a document and an external data store.
The NSPersistentDocument
class provides methods to access the document’s managed object context and provides implementations of the standard NSDocument
methods to read and write files that use the Core Data framework. By default you do not have to write any additional code to handle object persistence. A persistent document’s undo functionality is integrated with the managed object context.
In order both to manage the object graph and to support object persistence, Core Data needs a rich description of the objects it operates on. A managed object model is a schema that provides a description of the managed objects, or entities, used by your application, as illustrated in Figure 6. You typically create the managed object model graphically using Xcode's Data Model Design tool. (If you wish you can construct the model programmatically at runtime.)
The model is composed of a collection of entity description objects that each provide metadata about an entity, including the entity's name, the name of the class that represents it in your application (this does not have to be the same as its name), and its attributes and relationships. The attributes and relationships in turn are represented by attribute and relationship description objects, as illustrated in Figure 7.
Managed objects must be instances of either NSManagedObject
or of a subclass of NSManagedObject
. NSManagedObject
implements all the basic behavior required of a managed object. A managed object has a reference to the entity description for the entity of which it is an instance. It refers to the entity description to discover metadata about itself, including the name of the entity it represents and information about its attributes and relationships.
NSManagedObject
is able to represent any entity. It uses a private internal store to maintain its properties. You can access the properties using key-value coding—the object refers to the entity description to determine what are valid keys. You need to create a subclass of NSManagedObject
(and hence write source code) only if you want to implement additional behavior or if you want to use attribute types that Core Data does not support. For example, if you want to use custom accessors for properties—so that you could, say, invoke a salary
method rather than rely on key-value coding —you would implement a subclass of NSManagedObject
for the Employee entity. Similarly, if you want to specify a fullName
method that returns a concatenation of an Employee's first and last names, you use a subclass. Core Data natively supports only a limited—but useful!—set of types of attribute (NSString
, NSNumber
, NSData
, and NSDate
). If you want to represent the salary attribute of an Employee using a float
or if you want to use a custom class to represent an employee's photograph, again you do so with a subclass.
NSManagedObject
's ability to represent any entity presents another significant benefit. In traditional Cocoa application development, you need to create object classes to represent the model objects. This involves hand-coding classes with instance variables and, typically, suitable accessor methods. As your application evolves and class and variable names change, you have to rewrite the corresponding source files. Using the Core Data framework, instead of hand-coding concrete classes you can often simply use NSManagedObject
.
Having given an overview of what Core Data is, it is also useful to correct some common misperceptions and state what it is not.
Core Data is not a relational database or a relational database management system (RDBMS). Core Data provides an infrastructure for change management and for saving objects to and retrieving them from storage. It can use SQLite as one of its persistent store types. It is not, though, in and of itself a database.
To further illustrate this point, you could for example use just an in-memory store in your application. You could use Core Data for change tracking and management, but never actually save any data in a file.
Core Data is not a silver bullet. It does not remove the need to write code. Although it is possible to create a sophisticated application solely using the Xcode data modeling tool and Interface Builder, for more real-world applications you will still have to write code.
Core Data does not rely on Cocoa bindings. Core Data integrates well with Cocoa bindings and leverages the same technologies—and used together they can significantly reduce the amount of code you have to write—but it is possible to use Core Data without bindings. You can readily create a Core Data application without a user interface (see Core Data Utility Tutorial).
© 2004, 2009 Apple Inc. All Rights Reserved. (Last updated: 2009-03-04)