This article summarizes some of the new features and changes in functionality in Core Data on Mac OS X v10.5 (Leopard).
Taking Advantage of New Features: Linking
Store Versioning and Migration
Persistent Store API
64-bit and Garbage Collection
NSManagedObject
Fetching
Performance and Multi-Threading
Performance Analysis
Deleting Objects
SQLite Store Options
NSErrorMergePolicy and Optimistic Locking Records
Managed Object Context, save:, and commitEditing
Core Data provides a number of major new features and enhancements on Mac OS X v10.5. You can take advantage of these on Mac OS X v10.5 while still maintaining backwards compatibility with Mac OS X v10.4 provided that you link your application appropriately.
There are two ways you can build (on Mac OS X v10.5) a project that runs on both Mac OS X v10.4 and Mac OS X v10.5:
Build a Tiger project against the Mac OS X v10.4 SDK.
Build a Leopard project against the Mac OS X v10.5 SDK and set the "Mac OS X Deployment Target" to Mac OS X v10.4.
The first option is what most developers do by default. This means, however, that even on Mac OS X v10.5 the application runs as a Mac OS X v10.4 application and cannot use new features or exclusive bug fixes. To take advantage of the new features and bug fixes, you must build your project against the Mac OS X v10.5 SDK and set the "Mac OS X Deployment Target" to Mac OS X v10.4.
One of the major new Leopard features is an architecture to support versioning and migration—see Core Data Model Versioning and Data Migration Programming Guide for more details.
One of the major new Leopard features is a way that you can create your own persistent store type. You use the new NSPersistentStore
and NSAtomicStore
classes—see Atomic Store Programming Topics
for more details. Persistent stores in general are discussed in Persistent Stores
and Using Persistent Stores
.
On Mac OS X v10.5, Core Data is fully 64-bit compliant and fully GC-compliant.
On Mac OS X v10.5, NSManagedObject
is a class cluster. In practice, this should typically make no difference to the code you write. It does mean, however, that you must
use the proper Cocoa patterns if you override an initializer—that is, you must ensure that you set self
to the return value from invocation of super’s implementation, as shown in the following example:
- (id)initWithEntity:(NSEntityDescription*)entityinsertIntoManagedObjectContext:(NSManagedObjectContext*)context |
{ |
if (self = [super initWithEntity:entity insertIntoManagedObjectContext:context]) |
{ |
// perform additional initialization |
} |
return self; |
} |
primitiveValueForKey:
no longer supports unmodeled properties—there is no support for undefined keys.
NSManagedObject
uses dynamic class generation to support the Objective-C 2 properties feature (see “Objective-C 2 Support”
) by automatically creating a subclass of your class appropriate for your entity. This is also typically transparent to you, however whereas NSManagedObject
’s class
method returns your class, the Objective-C function object_getClass
returns the dynamically-generated class.
As a class cluster, NSManagedObject
's alloc
/initWithEntity:insertIntoManagedObjectContext:
pair will always return an instance of correct class for the entity you pass as the parameter. The dynamically-generated subclass will be based on the class specified by the entity, so specifying a custom class in your model will supersede the class passed to alloc
.
There's a new "transformable" type for NSManagedObject
attributes that allows you more easiliy support attribute types that Core Data doesn't support natively. You access an attribute as a non-standard type, but behind the scenes Core Data uses an instance of NSValueTransformer
to convert the attribute to and from an instance of NSData
. Core Data then stores the data instance to the persistent store.
If you don't specify a transformer, transformable attributes to use keyed archiving (NSKeyedUnarchiveFromDataTransformerName
). Note that this is a change from pre-release Leopard seeds which instead used serial archiving.
For more details, see Non-Standard Attributes .
You can use Objective-C declared properties (see Properties
) in custom subclasses of NSManagedObject
.
On Mac OS X v10.5, Core Data will dynamically generate extremely efficient public and primitive get and set attribute accessor methods and relationship accessor methods for managed object classes. These are described in detail in Managed Object Accessor Methods , but this is a summary of the most important points:
On Mac OS X v10.5, an instance of NSManagedObject
always responds to accessor methods for all its modeled properties. Accessor methods are the recommended way of accessing properties of a managed object.
You are encouraged to use the Objective-C dynamic
@property
declaration for compiler support and type checking, but this is not required to use this feature.
If you do need to write custom accessor methods, instead of primitiveValueForKey:
and setPrimitiveValue:forKey:
you should use primitive<PropertyName>
and setPrimitive<PropertyName>:
(the latter are considerably more efficient).
Note that on Mac OS X v10.5, primitiveValueForKey:
no longer supports unmodeled keys.
In addition to standard getter/setter methods, the accessors support the KVC mutable proxy names for to-many relationships.
If you have two subclasses of NSManagedObject
, and the parent class implements a dynamic property, and its subclass (the grandchild of NSManagedObject
) overrides the methods for the property, those overrides cannot call super.
@interface Parent : NSManagedObject |
@property(retain) NSString* foo; |
@end |
@implementation Parent |
@dynamic foo; |
@end |
@interface Child : Parent |
@end |
@implementation Child |
- (NSString*) foo |
{ |
// this throws: selector not found |
return super.foo; |
} |
@end |
When you create a fetch request, you can use setRelationshipKeyPathsForPrefetching:
to specify key paths for relationships that should be fetched with the target entity. There are numerous other new configuration options for fetch requests—see NSFetchRequest
for full details.
The new API for NSFetchRequest
can be extremely helpful in working with data between threads. For example, you can configure a fetch request to return object IDs but exclude the row data (and update the row cache)—this can be useful if you're just going to pass those object IDs from a background thread to another thread. You can also pre-fetch relationships (which avoids the issue described in “Pre-fetching” in Performance
) and pre-populate managed objects (that is, disable lazy initialization—which avoids the issue described in “Batch faulting” in Performance
).
On Mac OS X v10.5, executeFetchRequest:error:
intrinsically scales its behavior appropriately for the hardware and work load. If necessary, the Core Data will create additional private threads to optimize fetching performance. You will not now improve absolute fetching speed by creating background threads (although it may still be appropriate to fetch in a background thread for enhanced responsiveness—that is, to prevent your application from blocking).
Core Data now tries to optimize stack tear down (destruction of the persistent store coordinator, persistent stores, and so on). When a stack is torn down, the dealloc
method of the component objects may not be called. You should therefore not do resource cleanup (such as removal of temporary files) in a dealloc
method, instead you should invalidate resources when they are no longer needed (for a discussion of this subject as it pertains to garbage collection, see Adopting Garbage Collection
in Garbage Collection Programming Guide
).
Changes have been made to the merge policies to improve robustness and avoid resurrecting certain objects. On Mac OS X v10.4, there were issues with objects that have with relationships to deleted objects, when the delete rules would have cascaded had all the changes been made in the same context. On Mac OS X v10.5, merge policies handle delete propagation cascades correctly even between processes with disparate updates.
Core Data now provides support for various dtrace
probes, which can also be used with Instruments—see “Analyzing Performance” in Performance
for more information.
On Mac OS X v10.4, you could not call deleteObject:
in a set accessor method; on Mac OS X v10.5 you can.
On Mac OS X v10.4, you could not delete an object if it was a fault that could not be fulfilled (that is, already deleted by another context or process). On Mac OS X v10.5, you can always delete faults to missing records.
On Mac OS X v10.5, Core Data won’t let you save the object graph if
in the validation stage it contains a reference to a deleted object. For example, during a save operation Core Data will generate a deny error if a deleted object has a relationship to another object. This may happen especially if you haven’t specified a relationship without an inverse, or if after propagating deletions you add a reference to a deleted object. This is a change in behavior from Mac OS X v10.4 (Mac OS X v10.4 would not generate deny errors). Note that if you manually perform validation, you may need to call processPendingChanges
first so that deletions and other coalesced operations cascade completely to purge any references to pending deletions from the graph.
On Mac OS X v10.4, there are only two settings to control the way in which data in a SQLite-based store is written to disk. In order to provide finer granularity of control over the tradeoff between performance and reliability, on Mac OS X v10.5 Core Data uses two independent pragmas to control these options.
Note that the default fsync
behavior on Tiger was fcntl(F_FULLFSYNC)
but on Leopard it is now a standard fsync
. (This affects all SQLite databases on Leopard, not just Core Data.) The pragma allows you to toggle this value. See “Configuring a SQLite Store’s Save Behavior” in Persistent Stores
for a full discussion.
In addition, the SQLite store now supports:
Additional SQL generation for predicates including ANY and IN operators and sub-queries;
A time-out option on the persistent store coordinator.
The NSErrorMergePolicy
policy causes a save to fail if there are any merge conflicts (see “Conflict Detection and Optimistic Locking” and “Conflict Resolution” in Change Management
). In the case of failure, the save:
method returns with an error with a user info dictionary that contains the key @"conflictList"
; the corresponding value is an array of conflict records.
On Mac OS X v10.4, all relationship values in the records are managed objects; on Mac OS X v10.5, all relationship values in the records to be objectIDs. This change is backwards binary compatible, so this only affects compiled on Mac OS X v10.5 and using the NSErrorMergePolicy
to perform some kind of custom recovery.
The behavior of NSManagedObjectContext
’s save:
method has changed in Mac OS X v10.5. On Mac OS X v10.4, the save:
method erroneously caused bound text views to commit their pending edits ; on Mac OS X v10.5 this error has been fixed.
This means that, for example, in Core Data non-document template based applications linked on or after Mac OS X v10.5, to replicate the behavior of a Mac OS X v10.4 application, the application delegate needs to send the managed object context a commitEditing
message when saving.
The CoreData application template provided with Xcode includes an application delegate that implements some basic Core Data and application functionality, including a save action. The implementation of the saveAction:
method is simple—it just tells the delegate's managed object context to save. For user interfaces created with Cocoa Bindings and linked on Mac OS X v10.5, this has the effect of discarding editing before doing the actual save. For applications linked on Mac OS X v10.4, this behavior triggered a bug where bound text views would have their pending edits committed
instead of discarded.
This has been fixed for applications linked on or after Mac OS X v10.5. However, this does mean that if you want pending edits in bound text views to be committed during a save you need to modify the saveAction:
method to first call [[self managedObjectContext] commitEditing]
(or use the commitEditingWithDelegate:didCommitSelector:contextInfo:
variant) before saving. Otherwise the pending edits will be discarded.
© 2007 Apple Inc. All Rights Reserved. (Last updated: 2007-12-11)
|