< Previous PageNext Page > Hide TOC

Managed Object Accessor Methods

This article explains why you might want to implement custom accessor methods for managed objects, and how to implement them for attributes and for relationships. It also illustrates how to implement primitive accessor methods.

Contents:

Overview
Dynamically-Generated Accessor Methods
Custom Attribute and To-One Relationship Accessor Methods
Custom To-Many Relationship Accessor Methods
Custom Primitive Accessor Methods


Overview

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 for you to write accessor methods for properties that are defined in the entity of a managed object’s corresponding managed object model—although you may use the Objective-C 2 properties feature to declare properties to suppress compiler warnings. To get the best performance—and to benefit from type-checking—you use the accessor methods directly, although they are also key-value coding (KVC) compliant so if necessary you can use standard key-value coding methods such as valueForKey:. You do need to write custom accessor methods if you use transient properties to support non-standard data types (see “Non-Standard Persistent Attributes”) or if you use scalar instance variables to represent an attribute.

Mac OS X v10.4: This article describes accessor methods for Mac OS X v10.5; if you are using Mac OS X v10.4, see “Mac OS X v10.4: Managed Object Accessor Methods.”

Custom implementation

The implementation of accessor methods you write for subclasses of NSManagedObject is typically different from those you write for other classes.

You can use the Xcode data modeling tool to generate the code for accessor methods for any modeled property.

Key-value coding access pattern

The access pattern key-value coding uses for managed objects is largely the same as that used for subclasses of NSObject—see valueForKey:. The difference is that, if after checking the normal resolutions valueForKey: would throw an unbound key exception, the key-value coding mechanism for NSManagedObject checks whether the key is a modeled property. If the key matches an entity's property, the mechanism looks first for an accessor method of the form primitiveKey, and if that is not found then looks for a value for key in the managed object's internal storage. If these fail, NSManagedObject throws an unbound key exception (just like valueForKey:).

Dynamically-Generated Accessor Methods

By default, Core Data dynamically creates efficient public and primitive get and set accessor methods for modeled properties (attributes and relationships) of managed object classes. This includes the key-value coding mutable proxy methods such as add<Key>Object: and remove<Key>s:, as detailed in the documentation for mutableSetValueForKey:—managed objects are effectively mutable proxies for all their to-many relationships.

Note: If you choose to implement your own accessors, the dynamically-generated methods never replace your own code.

For example, given an entity with an attribute firstName, Core Data automatically generates firstName, setFirstName:, primitiveFirstName, and setPrimitiveFirstName:. Core Data does this even for entities represented by NSManagedObject. To suppress compiler warnings when you invoke these methods, you should use the Objective-C 2.0 declared properties feature (see Declared Properties), as described in “Declaration.”

The property accessor methods Core Data generates are by default (nonatomic, retain)this is the recommended configuration. The methods are nonatomic because non-atomic accessors are more efficient than atomic accessors, and in general it is not possible to assure thread safety in a Core Data application at the level of accessor methods. (To understand how to use Core Data in a multi-threaded environment, see Multi-Threading with Core Data.)

In addition to always being nonatomic, dynamic properties only honor retain or copy attributes—assign is treated as retain. You should use copy sparingly as it increases overhead. You cannot use copy for relationships because NSManagedObject does not adopt the NSCopying protocol, and it's irrelevant to the behavior of to-many relationships.

Important: If you specify copy for a to-one relationship, you will generate a run-time error.

Declaration

You can use Objective-C 2 properties to declare properties of managed object classes—you typically do this so that you can use the default accessors Core Data provides without generating compiler warnings. The easiest way to generate the declarations is to select the relationship in the Xcode modeling tool and choose Design > Data Model > Copy Obj-C 2.0 Method Declarations to Clipboard. and then modify the code if necessary.

You declare attributes and relationships as you would properties for any other object, as illustrated in the following example. When you declare a to-many relationship, the property type should be NSSet *. (The value returned from the get accessor is not a KVO-compliant mutable proxy—for more details, see “To-many relationships.”)

@interface Employee : NSManagedObject
{ }
@property(nonatomic, retain) NSString* firstName, lastName;
@property(nonatomic, retain) Department* department;
@property(nonatomic, retain) Employee* manager;
@property(nonatomic, retain) NSSet* directReports;
@end

If you are not using a custom class, you can declare properties in a category of NSManagedObject:

@interface NSManagedObject (EmployeeAccessors)
{ }
@property(nonatomic, retain) NSString* firstName, lastName;
@property(nonatomic, retain) Department* department;
@property(nonatomic, retain) Employee* manager;
@property(nonatomic, retain) NSSet* directReports;
@end

You can use the same techniques to suppress compiler warnings for the automatically-generated to-many relationship mutator methods, for example:

@interface Employee (DirectReportsAccessors)
 
- (void)addDirectReportsObject:(Employee *)value;
- (void)removeDirectReportsObject:(Employee *)value;
- (void)addDirectReports:(NSSet *)value;
- (void)removeDirectReports:(NSSet *)value;
 
@end

You typically retain attributes, although to preserve encapsulation where the attribute class has a mutable subclass and it implements the NSCopying protocol you can also use copy, for example:

@property(nonatomic, copy) NSString* firstName, lastName;

Implementation

You can specify an implementation using the @dynamic keyword, as shown in the following example—although since @dynamic is the default, there is no need to do so:

@dynamic firstName, lastName;
@dynamic department, manager;
@dynamic directReports;

There should typically be no need for you to provide your own implementation of these methods, unless you want to support scalar values. The methods that Core Data generates at runtime are more efficient than those you can implement yourself.

Inheritance

If you have two subclasses of NSManagedObject where 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(nonatomic, retain) NSString* parentString;
@end
 
@implementation Parent
@dynamic parentString;
@end
 
@interface Child : Parent
@end
 
@implementation Child
- (NSString *)parentString
{
    // this throws a "selector not found" exception
    return parentString.foo;
}
@end

Custom Attribute and To-One Relationship Accessor Methods

Important: You are strongly encouraged to use dynamic properties (that is, properties whose implementation you specify as @dynamic) instead of creating custom implementations for standard or primitive accessor methods.

If you want to implement your own attribute or to-one relationship accessor methods, you use the primitive accessor methods to get and set values from and to the managed object's private internal store. You must invoke the relevant access and change notification methods, as illustrated in Listing 1. NSManagedObject's implementation of the primitive set accessor method handles memory management for you.

Listing 1  Implementation of a custom managed object class illustrating attribute accessor methods

@interface Department : NSManagedObject
{
}
@property(nonatomic, retain) NSString *name;
@end
 
@interface Department (PrimitiveAccessors)
- (NSString *)primitiveName;
- (void)setPrimitiveName:(NSString *)newName;
@end
 
 
@implementation Department
 
@dynamic name;
 
- (NSString *)name
{
    [self willAccessValueForKey:@"name"];
    NSString *myName = [self primitiveName];
    [self didAccessValueForKey:@"name"];
    return myName;
}
 
- (void)setName:(NSString *)newName
{
    [self willChangeValueForKey:@"name"];
    [self setPrimitiveName:newName];
    [self didChangeValueForKey:@"name"];
}
@end

The default implementation does not copy attribute values. If the attribute value may be mutable and implements the NSCopying protocol (as is the case with NSString, for example), you can copy the value in a custom accessor to help preserve encapsulation (for example, in the case where an instance of NSMutableString is passed as a value). This is illustrated in Listing 2. Notice also that (for the purposes of illustration) in this example the get accessor is not implemented—since it’s not implemented, Core Data will generate it automatically.

Listing 2  Implementation of a custom managed object class illustrating copying setter

@interface Department : NSManagedObject
{
}
@property(nonatomic, copy) NSString *name;
@end
 
@implementation Department
 
@dynamic name;
 
- (void)setName:(NSString *)newName
{
    [self willChangeValueForKey:@"name"];
    // NSString implements NSCopying, so copy the attribute value
    NSString *newNameCopy = [newName copy];
    [self setPrimitiveName:newNameCopy];
    [newNameCopy release];
    [self didChangeValueForKey:@"name"];
}
@end

If you choose to represent an attribute using a scalar type (such as NSInteger or CGFloat), or as one of the structures supported by NSKeyValueCoding (NSRect, NSPoint, NSSize, NSRange), then you should implement accessor methods as illustrated in Listing 3. If you want to use any other attribute type, then you should use a different pattern, described in Non-Standard Persistent Attributes.

Listing 3  Implementation of a custom managed object class illustrating a scalar attribute value

@interface Circle : NSManagedObject
{
    CGFloat radius;
}
@property CGFloat radius;
@end
 
@implementation Circle
 
- (CGFloat)radius
{
    [self willAccessValueForKey:@"radius"];
    float f = radius;
    [self didAccessValueForKey:@"radius"];
    return f;
}
 
- (void)setRadius:(CGFloat)newRadius
{
    [self willChangeValueForKey:@"radius"];
    radius = newRadius;
    [self didChangeValueForKey:@"radius"];
}
@end

Custom To-Many Relationship Accessor Methods

Important: You are strongly encouraged to use dynamic properties (that is, properties whose implementation you specify as @dynamic) instead of creating custom implementations for standard or primitive accessor methods.

You usually access to-many relationships using mutableSetValueForKey:, which returns a proxy object that both mutates the relationship and sends appropriate key-value observing notifications for you. There should typically be little reason to implement your own collection accessor methods for to-many relationships. If they are present, however, the framework calls the mutator methods (such as add<Key>Object: and remove<Key>Object:) when modifying a collection that represents a persistent relationship. (Note that fetched properties do not support the mutable collection accessor methods.) In order for this to work correctly, you must implement an add<Key>Object:/remove<Key>Object: pair, an add<Key>:/remove<Key>: pair, or both pairs. You may also implement other get accessors (such as countOf<Key>:, enumeratorOf<Key>:, and memberOf<Key>:) and use these in your own code, however these are not guaranteed to be called by the framework.

Important: For performance reasons, the proxy object returned by managed objects for mutableSetValueForKey: does not support set<Key>: style setters for relationships. For example, if you have a to-many relationship employees of a Department class and implement accessor methods employees and setEmployees:, then manipulate the relationship using the proxy object returned by mutableSetValueForKey:@"employees", setEmployees: is not invoked. You should implement the other mutable proxy accessor overrides instead.

If you do implement collection accessors for model properties, they must invoke the relevant KVO notification methods. Listing 4 illustrates the implementation of accessor methods for a to-many relationship—employees—of a Department class. The easiest way to generate the implementation is to select the relationship in the Xcode modeling tool and choose Design > Data Model > Copy Obj-C 2.0 Method {Declarations/Implementations} to Clipboard.

Listing 4  A managed object class illustrating implementation of custom accessors for a to-many relationship

@interface Department : NSManagedObject
{
}
@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSSet *employees;
@end
 
 
@interface Department (DirectReportsAccessors)
 
- (void)addEmployeesObject:(Employee *)value;
- (void)removeEmployeesObject:(Employee *)value;
- (void)addEmployees:(NSSet *)value;
- (void)removeEmployees:(NSSet *)value;
 
- (NSMutableSet*)primitiveEmployees;
- (void)setPrimitiveEmployees:(NSMutableSet*)value;
 
@end
 
 
@implementation Department
 
@dynamic name;
@dynamic employees;
 
- (void)addEmployeesObject:(Employee *)value
{
    NSSet *changedObjects = [[NSSet alloc] initWithObjects:&value count:1];
 
    [self willChangeValueForKey:@"employees"
          withSetMutation:NSKeyValueUnionSetMutation
          usingObjects:changedObjects];
    [[self primitiveEmployees] addObject:value];
    [self didChangeValueForKey:@"employees"
          withSetMutation:NSKeyValueUnionSetMutation
          usingObjects:changedObjects];
 
    [changedObjects release];
}
 
- (void)removeEmployeesObject:(Employee *)value
{
    NSSet *changedObjects = [[NSSet alloc] initWithObjects:&value count:1];
 
    [self willChangeValueForKey:@"employees"
          withSetMutation:NSKeyValueMinusSetMutation
          usingObjects:changedObjects];
    [[self primitiveEmployees] removeObject:value];
    [self didChangeValueForKey:@"employees"
          withSetMutation:NSKeyValueMinusSetMutation
          usingObjects:changedObjects];
 
    [changedObjects release];
}
 
- (void)addEmployees:(NSSet *)value
{
    [self willChangeValueForKey:@"employees"
          withSetMutation:NSKeyValueUnionSetMutation
          usingObjects:value];
    [[self primitiveEmployees] unionSet:value];
    [self didChangeValueForKey:@"employees"
          withSetMutation:NSKeyValueUnionSetMutation
          usingObjects:value];
}
 
- (void)removeEmployees:(NSSet *)value
{
    [self willChangeValueForKey:@"employees"
          withSetMutation:NSKeyValueMinusSetMutation
          usingObjects:value];
    [[self primitiveEmployees] minusSet:value];
    [self didChangeValueForKey:@"employees"
          withSetMutation:NSKeyValueMinusSetMutation
          usingObjects:value];
}

Custom Primitive Accessor Methods

Primitive accessor methods are similar to "normal" or public key-value coding compliant accessor methods, except that Core Data uses them as the most basic data methods to access data, consequently they do not issue key-value access or observing notifications. Put another way, they are to primitiveValueForKey: and setPrimitiveValue:forKey: what public accessor methods are to valueForKey: and setValue:forKey:.

Typically there should be little reason to implement primitive accessor methods. They are, however, useful if you want custom methods to provide direct access to instance variables for persistent Core Data properties. The example below contrasts public and primitive accessor methods for an attribute, int16, of type Integer 16, stored in a custom instance variable, nonCompliantKVCivar.

// primitive get accessor
- (short)primitiveInt16 {
    return nonCompliantKVCivar;
}
 
// primitive set accessor
- (void)setPrimitiveInt16:(short)newInt16 {
    nonCompliantKVCivar = newInt16;
}
 
// public get accessor
- (short)int16 {
    short tmpValue;
    [self willAccessValueForKey: @"int16"];
    tmpValue = nonCompliantKVCivar;
    [self didAccessValueForKey: @"int16"];
    return tmpValue;
}
 
// public set accessor
- (void)setInt16:(short)int16 {
    [self willChangeValueForKey: @"int16"];
    nonCompliantKVCivar = int16;
    [self didChangeValueForKey:@"int16"];
}


< Previous PageNext Page > Hide TOC


© 2004, 2009 Apple Inc. All Rights Reserved. (Last updated: 2009-03-04)


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.