This article provides basic information about what is a managed object, how its is data stored, how you implement a custom managed object class, object life-cycle issues, and faulting. There are several other articles in the Core Data Programming Guide that describe other aspects of using managed objects:
Basics
Properties and Data Storage
Custom Managed Object Classes
Object Life-Cycle—Initialization and Deallocation
Validation
Faulting
Managed objects are instances of the NSManagedObject
class, or of a subclass of NSManagedObject
, that represent instances of an entity. NSManagedObject
is a generic class that implements all the basic behavior required of a managed object. You can create custom subclasses of NSManagedObject
, although this is often not required. If you do not need any custom logic for a given entity, you do not need to create a custom class for that entity. You might implement a custom class, for example, to provide custom accessor or validation methods, to use non-standard attributes, to specify dependent keys, to calculate derived values, or to implement any other custom logic.
A managed object is associated with an entity description (an instance of NSEntityDescription
) that provides metadata about the object (including the name of the entity that the object represents and the names of its attributes and relationships) and with a managed object context that tracks changes to the object graph.
It is important to understand that a managed object is an abstraction, and works in conjunction with a managed object context (“context”). In a given context, a managed object provides a representation of data records in a persistent store. In a given context, for a given record in a persistent store, there can be only one corresponding managed object, but there may be multiple contexts each containing a separate managed object representing that record. Put another way, there is a to-one relationship between a managed object and the data record it represents, but a to-many relationship between the data record and corresponding managed objects.
The context acts as a scratchpad. You can create and register managed objects with it, make changes to the objects, and undo and redo changes as you wish. If you make changes to managed objects associated with a given context, those changes remain local to that context until you commit the changes by sending the context a save:
message. At that point—provided that there are no validation errors—the changes are committed to the store. As a corollary, simply creating a managed object does not cause it to be saved to a persistent store, and deleting a managed object does not cause the record to be removed from the store—you must save the context to commit the change.
In some respects, an NSManagedObject
acts like a dictionary—it is a generic container object that efficiently provides storage for the properties defined by its associated NSEntityDescription
object. NSManagedObject
provides support for a range of common types for attribute values, including string, date, and number (see NSAttributeDescription
for full details). There is therefore commonly no need to define instance variables in subclasses. There are some performance considerations to bear in mind if you use large binary data objects—see “Large Data Objects (BLOBs).”
NSManagedObject
provides support for a range of common types for attribute values, including string, date, and number (see NSAttributeDescription
for full details). By default, NSManagedObject
stores its properties as objects in an internal structure, and in general Core Data is more efficient working with storage under its own control rather using custom instance variables.
Sometimes you want to use types that are not supported directly, such as colors and C structures. For example, in a graphics application you might want to define a Rectangle entity that has attributes color and bounds that are an instance of NSColor
and an NSRect
struct respectively. This may require you to create a subclass of NSManagedObject
, and is described in “Non-Standard Persistent Attributes.”
NSManagedObject
represents date attributes using NSDate
objects, and stores times internally as an NSTimeInterval
value since the reference date (which has a time zone of GMT). Time zones are not explicitly stored—indeed you should always represent a Core Data date attribute in GMT, this way searches are normalized in the database. If you need to preserve the time zone information, you need to store a time zone attribute in your model. This may again require you to create a subclass of NSManagedObject
.
In combination with the entity description in the managed object model, NSManagedObject
provides a rich set of default behaviors including support for arbitrary properties and value validation. There are nevertheless many reasons why you might wish to subclass NSManagedObject
to implement custom features. There are also, however, some things to avoid when subclassing.
NSManagedObject
itself customizes many features of NSObject
so that managed objects can be properly integrated into the Core Data infrastructure. Core Data relies on NSManagedObject
’s implementation of the following methods, which you should therefore not override: primitiveValueForKey:
, setPrimitiveValue:forKey:
, isEqual:
, hash
, superclass
, class
, self
, zone
, isProxy
, isKindOfClass:
, isMemberOfClass:
, conformsToProtocol:
, respondsToSelector:
, retain
, release
, autorelease
, retainCount
, managedObjectContext
, entity
, objectID
, isInserted
, isUpdated
, isDeleted
, and isFault
. You are discouraged from overriding description
—if this method fires a fault during a debugging operation, the results may be unpredictable—and initWithEntity:insertIntoManagedObjectContext:
. You should typically not override the key-value coding methods such as valueForKey:
and setValue:forKeyPath:
.
In addition to methods you should not override, there are others that if you do override you should invoke the superclass’s implementation first, including awakeFromInsert
, awakeFromFetch
, and validation methods such as validateForUpdate:
.
On Mac OS X v10.5, Core Data dynamically generates efficient public and primitive get and set attribute accessor methods and relationship accessor methods for managed object classes. Typically, therefore, there’s no need to write custom accessor methods for properties that are defined in the entity of a managed object’s corresponding managed object model. Should you wish to do so, however, there are several implementation patterns you must follow; these are described in “Managed Object Accessor Methods.” (If you are using Mac OS X v10.4, Core Data does not dynamically generate accessor methods—for implementation patterns, see “Mac OS X v10.4: Managed Object Accessor Methods.”)
It is important to appreciate that Core Data "owns" the life-cycle of managed objects. With faulting and undo, you cannot make the same assumptions about the life-cycle of a managed object as you would of a standard Cocoa object—managed objects can be instantiated, destroyed, and resurrected by the framework as it requires.
When a managed object is created, it is initialized with the default values given for its entity in the managed object model. In many cases the default values set in the model may be sufficient. Sometimes, however, you may wish to perform additional initialization—perhaps using dynamic values (such as the current date and time) that cannot be represented in the model.
In a typical Cocoa class, you usually override the designated initializer (often the init
method). In a subclass of NSManagedObject
, there are three different ways you can customize initialization —by overriding initWithEntity:insertIntoManagedObjectContext:
, awakeFromInsert
, or awakeFromFetch
. You should not override init
. You are discouraged from overriding initWithEntity:insertIntoManagedObjectContext:
as state changes made in this method may not be properly integrated with undo and redo. The two other methods, awakeFromInsert
and awakeFromFetch
, allow you to differentiate between two different situations.
awakeFromInsert
is invoked only once in the lifetime of an object—when it is first created (immediately after you invoke initWithEntity:insertIntoManagedObjectContext:
or insertNewObjectForEntityForName:inManagedObjectContext:
. You can use awakeFromInsert
to initialize special default property values, such as the creation date of an object, as illustrated in the following example.
- (void) awakeFromInsert |
{ |
[super awakeFromInsert]; |
[self setCreationDate:[NSDate date]]; |
} |
awakeFromFetch
is invoked when an object is re-initialized from a persistent store (during a fetch). You can override it to, for example, establish transient values and other caches. Change processing is explicitly disabled around awakeFromFetch
so that you can conveniently use public set accessor methods without dirtying the object or its context. This does mean, however, that you should not manipulate relationships, as changes will not be properly propagated to the destination object or objects. Instead, you can override awakeFromInsert
or employ any of the run loop related methods such as performSelector:withObject:afterDelay:
.
You should typically not override dealloc
or finalize
to clear transient properties and other variables. Instead, you should override didTurnIntoFault
. didTurnIntoFault
is invoked automatically by Core Data when an object is turned into a fault and immediately prior to actual deallocation. You might turn a managed object into a fault specifically to reduce memory overhead (see “Reducing Memory Overhead”), so it is important to ensure that you properly perform clean-up operations in didTurnIntoFault
.
NSManagedObject
provides consistent hooks for validating property and inter-property values. You typically should not override validateValue:forKey:error:
, instead you should implement methods of the form validate<Key>:error:
, as defined by the NSKeyValueCoding
protocol. If you want to validate inter-property values, you can override validateForUpdate:
and/or related validation methods.
You should not call validateValue:forKey:error:
within custom property validation methods—if you do so you will create an infinite loop when validateValue:forKey:error:
is invoked at runtime. If you do implement custom validation methods, you should typically not call them directly. Instead you should call validateValue:forKey:error:
with the appropriate key. This ensures that any constraints defined in the managed object model are applied.
If you implement custom inter-property validation methods (such as validateForUpdate:
), you should call the superclass’s implementation first. This ensures that individual property validation methods are also invoked. If there are multiple validation failures in one operation, you should collect them in an array and add the array—using the key NSDetailedErrorsKey
—to the userInfo dictionary in the NSError
object you return.
Managed objects typically represent data held in a persistent store. In some situations a managed object may be a “fault”—an object whose property values have not yet been loaded from the external data store—see “Faulting and Uniquing” for more details. When you access persistent property values, the fault “fires” and the data is retrieved from the store automatically. This can be a comparatively expensive process (potentially requiring a round trip to the persistent store), and you may wish to avoid unnecessarily firing a fault (see “Faulting Behavior”).
Although the description
method does not cause a fault to fire, if you implement a custom description
method that accesses the object’s persistent properties, this will cause a fault to fire. You are strongly discouraged from overriding description
in this way.
Note that there is no way to load individual attributes of a managed object on an as-needed basis. For patterns to deal with large attributes, see “Large Data Objects (BLOBs).”
© 2004, 2009 Apple Inc. All Rights Reserved. (Last updated: 2009-03-04)