Managed Object Models

Much of Core Data's functionality depends on the schema you create to describe your application's entities, their properties, and the relationships between them. The schema is represented by a managed object model—an instance of NSManagedObjectModel. In general, the richer the model, the better Core Data is able to support your application. This article describes the features of a managed object model, how you create one, and how you use it in your application.

Features of a Managed Object Model

A managed object model is an instance of the NSManagedObjectModel class. It describes a schema—a collection of entities—that you use in your application. (If you do not understand the term "entity"—or the related terms, "property," "attribute," and "relationship"—you should first read “Core Data Basics” and “Object Modeling”.)

Entities

A model contains NSEntityDescription objects that represent the model's entities. Two important features of an entity are its name, and the name of the class used to represent the entity at runtime. You should be careful to keep clear the differences between an entity, the class used to represent the entity, and the managed objects that are instances of that entity.

An NSEntityDescription object may have NSAttributeDescription and NSRelationshipDescription objects that represent the properties of the entity in the schema. An entity may also have fetched properties, represented by instances of NSFetchedPropertyDescription, and the model may have fetch request templates, represented by instances of NSFetchRequest.

In a model, entities may be arranged in an inheritance hierarchy, and entities may be specified as abstract.

Entity Inheritance

Entity inheritance works in a similar way to class inheritance, and is useful for the same reasons. If you have a number of entities that are similar, you can factor the common properties into a super-entity. Rather than specifying the same properties in several entities, you can define them in one and the sub-entities inherit them. For example, you might define a Person entity with attributes firstName and lastName, and sub-entities Employee and Customer which inherit those attributes.

In many cases, you also implement a custom class to correspond to the entity from which classes representing the sub-entities also inherit. Rather than implementing business logic common to all the entities several times over, you implement them in one place and they are inherited by the subclasses.

If you create a model using the data modeling tool in Xcode, you specify an entity's parent by selecting the name of the entity from the Parent pop-up menu in the entity Info pane, as shown in Figure 1.

Figure 1  Selecting a parent entity in Xcode
Selecting a parent entity in Xcode

If you want to create an entity inheritance hierarchy in code, you must build it top-down. You cannot set an entity’s super-entity directly, you can only set an entity’s sub-entities (using the method setSubentities:). To set a super-entity for a given entity, you must therefore set an array of sub-entities for that super entity and include the current entity in that array.

Abstract Entities

You can specify that an entity is abstract—that is, that you will not create any instances of that entity. You typically make an entity abstract if you have a number of entities that all represent specializations of (inherit from) a common entity which should not itself be instantiated. For example, in a drawing application you might have a Graphic entity that defines attributes for x and y coordinates, color, and drawing bounds. You never, though, instantiate a Graphic. Concrete sub-entities of Graphic might be Circle, TextArea, and Line.

Properties

An entity's properties are its attributes and relationships, including its fetched properties (if it has any). Amongst other features, each property has a name and a type. Attributes may also have a default value. A property name cannot be the same as any no-parameter method name of NSObject or NSManagedObject—for example, you cannot give a property the name “description” (see NSPropertyDescription).

Transient properties are properties that you define as part of the model, but which are not saved to the persistent store as part of an entity instance's data. Core Data does track changes you make to transient properties, so they are recorded for undo operations.

Attributes

Core Data natively supports a variety of attribute types, such as string, date, and integer (represented as instances of NSString, NSDate, and NSNumber respectively). If you want to use an attribute type that is not natively supported, you can use one of the techniques described in “Non-Standard Persistent Attributes.”

You can specify that an attribute is optional—that is, it is not required to have a value. In general, however, you are discouraged from doing so—especially for numeric values (typically you can get better results using a mandatory attribute with a default value—in the model—of 0). The reason for this is that SQL has special comparison behavior for NULL that is unlike Objective-C's nil. NULL in a database is not the same as 0, and searches for 0 will not match columns with NULL.

false == (NULL == 0)
false == (NULL != 0)

Moreover, NULL in a database is not equivalent to an empty string or empty data blob, either:

false == (NULL == @"")
false == (NULL != @"")

This has no bearing on relationships.

Relationships

Core Data supports to-one and to-many relationships, and fetched properties. Fetched properties represent weak, one-way relationships. In the employees and departments domain, a fetched property of a department might be "recent hires" (employees do not have an inverse to the recent hires relationship).

You can specify the optionality and cardinality of a relationship, and its delete rule. You should typically model a relationship in both directions. A many-to-many relationship is one in which a relationship and its inverse are both to-many. Relationships are described in greater detail in “Relationships and Fetched Properties.”

Fetch Request Templates

You use the NSFetchRequest class to describe fetch requests to retrieve objects from a persistent store. It is often the case that you want to execute the same request on multiple occasions, or execute requests that follow a given pattern but which contain variable elements (typically supplied by the user). For example, you might want to be able to retrieve all publications written by a certain author, perhaps after a date specified by the user at runtime.

You can predefine fetch requests and store them in a managed object model as named templates. This allows you to pre-define queries that you can retrieve as necessary from the model. Typically, you define fetch request templates using the Xcode data modeling tool (see Xcode Tools for Core Data). The template may include variables, as shown in Figure 2.

Figure 2  Xcode predicate builder
Xcode Predicate Builder

For more about using fetch request templates, see “Accessing and Using a Managed Object Model at Runtime.”

User Info Dictionaries

Many of the elements in a managed object model—entities, attributes, and relationships—have an associated user info dictionary. You can put whatever information you want into a user info dictionary, as key-value pairs. Common information to put into the user info dictionary includes version details for an entity, and values used by the predicate for a fetched property.

Configurations

A configuration has a name and an associated set of entities. The sets may overlap—that is, a given entity may appear in more than one configuration. You establish configurations programmatically using setEntities:forConfiguration: or using the Xcode data modeling tool (see Xcode Tools for Core Data), and retrieve the entities for a given configuration name using entitiesForConfiguration:.

You typically use configurations if you want to store different entities in different stores. A persistent store coordinator can only have one managed object model, so by default each store associated with a given coordinator must contain the same entities. To work around this restriction, you can create a model that contains the union of all the entities you want to use. You then create configurations in the model for each of the subsets of entities that you want to use. You can then use this model when you create a coordinator. When you add stores, you specify the different store attributes by configuration. When you are creating your configurations, though, remember that you cannot create cross-store relationships.