In general, when you use Core Data you should follow the traditional Cocoa guidelines relating to memory management. There are, however, some additional considerations.
Note: On Mac OS X v10.5 and later, you can use Core Data in a garbage-collected environment (see Garbage Collection Programming Guide). Discussion in this article that is strictly related to a managed memory environment does not apply if you use garbage collection (for example, if you use garbage collection then retain cycles—as discussed in “Breaking Relationship Retain Cycles”—are not a problem).
Instance and Data Life-Cycles
The Role of the Managed Object Context
Breaking Relationship Retain Cycles
Change and Undo Management
It is important to understand that the life-cycle of the data a managed object represents is largely independent of the lifetime of individual managed object instances. In order to add a record to a persistent store, you must allocate and initialize a managed object—and then save the managed object context. When you remove a record from a persistent store, you should ensure its corresponding managed object is eventually deallocated. In between these events, however, you can create and destroy any number of instances of a managed object that represent the same record in a given persistent store.
NSEntityDescription
provides a convenience method—insertNewObjectForEntityForName:inManagedObjectContext:
—to create a new managed object and insert it into an editing context. Although the method name includes the word “new,” it returns an autoreleased object. This appears to be in contravention of the Cocoa rule that “new” methods return retained objects, however in this case the object returned is of a different class than that to which the message is sent.
Managed objects know what managed object context they’re associated with, and managed object contexts know what managed objects they contain. By default, though, the references between a managed object and its context are weak—in a managed memory environment, neither object retains the other.
This means that in general you cannot rely on a context to ensure the longevity of a managed object instance, and you cannot rely on the existence of a managed object to ensure the longevity of a context. Put another way, just because you fetched an object doesn’t mean it will stay around. A managed object's lifetime is by default determined by the run loop—autoreleased managed objects will be deallocated when the run loop's autorelease pool is released.
The exception to this rule is that a managed object context maintains a strong reference to (in a managed memory environment it retains) any changed (inserted, deleted, and updated) objects until the pending transaction is committed (with a save:
) or discarded (with a reset
or rollback
). Note that the undo manager may also retain changed objects—see “Change and Undo Management.”
You can change a context’s default behavior such that it does retain its managed objects by sending it a setRetainsRegisteredObjects:
message (with the argument YES
)—this makes the managed objects’ lifetimes depend on the context’s. This can be a convenience if you are caching smaller data sets in memory—for example if the context controls a temporary set of objects that may persist beyond a single event cycle, such as when editing in a sheet. It can also be useful if you are using multiple threads and passing data between them—for example if you are performing a background fetch and passing object IDs to the main thread. The background thread needs to retain the objects it pre-fetched for the main thread until it knows the main thread has actually used the object IDs to fault local instances into itself.
You should typically use a separate container to retain only those managed objects you really need. You can use an array or dictionary, or an object controller (for example an NSArrayController
instance) that explicitly retains the objects it manages. The managed objects you don't need will then be deallocated when possible (for example, when relationships are cleared).
If you have finished with a managed object context, or for some other reason you want to “disconnect” a context from its persistent store coordinator, you should not set the context’s coordinator to nil
:
// this will raise an exception |
[myManagedObjectContext setPersistentStoreCoordinator:nil]; |
Instead, you should simply relinquish ownership of the context (in a managed memory environment you send it a release
message) and allow it to be deallocated normally.
When you have relationships between managed objects, each object maintains a strong reference to the object or objects to which it is related. In a managed memory environment, this causes retain cycles (see Object Ownership and Disposal) that can prevent deallocation of unwanted objects. To ensure that retain cycles are broken, when you're finished with an object you can use the managed object context method refreshObject:mergeChanges:
to turn it into a fault.
You typically use refreshObject:mergeChanges:
to refresh a managed object's property values. If the mergeChanges
flag is YES
, the method merges the object's property values with those of the object available in the persistent store coordinator. If the flag is NO
, however, the method simply turns an object back into a fault without merging, which causes it to release related managed objects. This breaks the retain cycle between that managed object and the other managed objects it had retained.
Note that, of course, before the objects are deallocated everything outside the graph that is retaining them must release them. See also “Change and Undo Management.”
Managed objects that have pending changes (insertions, deletions, or updates) are retained by their context until their context is sent a save:
, reset
, rollback
, or dealloc
message, or the appropriate number of undos to undo the change.
The undo manager associated with a context retains any changed managed objects. By default, the context's undo manager keeps an unlimited undo/redo stack. To limit your application's memory footprint, you should make sure that you scrub (using removeAllActions
) the context's undo stack as and when appropriate. Unless you retain a context's undo manager, it is deallocated with its context.
If you do not intend to use Core Data's undo functionality, you can reduce your application's resource requirements by setting the context’s undo manager to nil
. This may be especially beneficial for background worker threads, as well as for large import or batch operations.
© 2004, 2009 Apple Inc. All Rights Reserved. (Last updated: 2009-03-04)