< Previous PageNext Page > Hide TOC

Mac OS X v10.4: Managed Object Accessor Methods

On Mac OS X v10.5, Core Data dynamically generates accessor methods for you. For the benefit of developers who need to develop for earlier releases, this article describes how to write accessor methods on Mac OS X v10.4.

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:

Introduction
Attribute Accessor Methods
Relationship Accessor Methods
Primitive Accessor Methods


Introduction

There is in general no need to write custom accessor methods for properties that are defined in the entity of a managed object’s corresponding managed object model. You can access properties using standard key-value coding methods such as valueForKey:. It may be convenient to implement custom accessors to benefit from compile-time type checking and to avoid errors with misspelled key names. You do need 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.

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:).

Custom Accessors

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.

Attribute Accessor Methods

Attribute accessors 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
{
}
- (NSString *)name;
- (void)setName:(NSString *)newName
@end
 
@implementation Department
 
- (NSString *)name
{
    [self willAccessValueForKey:@"name"];
    NSString *n = [self primitiveValueForKey:@"name"];
    [self didAccessValueForKey:@"name"];
    return n;
}
 
- (void)setName:(NSString *)newName
{
    [self willChangeValueForKey:@"name"];
    [self setPrimitiveValue:newName forKey:@"name"];
    [self didChangeValueForKey:@"name"];
}
@end

Note, however, that 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), it is typically useful to 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). The setName: method shown in Listing 1 would be implemented as follows:

- (void)setName:(NSString *)newName
{
    [self willChangeValueForKey:@"name"];
    // NSString implements NSCopying, so copy the attribute value
    NSString *nameCopy = [newName copy];
    [self setPrimitiveValue:nameCopy forKey:@"name"];
    [nameCopy release];
    [self didChangeValueForKey:@"name"];
}
@end

If you choose to represent an attribute using a scalar type (such as int or float), 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 2  Implementation of a custom managed object class illustrating a scalar attribute value

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

Relationship 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 again call the relevant KVO notification methods. Listing 4 illustrates the implementation of accessor methods for a to-many relationship—employees—of a Department class.

Listing 3  Implementation of a custom managed object class illustrating a to-many relationship

@interface Department : NSManagedObject
{
}
- (void)addEmployeesObject:(Employee *)anEmployee;
- (void)addEmployees:(NSSet *)employeesToAdd;
- (void)removeEmployeesObject:(Employee *)anEmployee;
- (void)removeEmployees:(NSSet *)employeesToRemove;
- (void)intersectEmployees:(NSSet *)employeesToIntersect;
@end
 
@implementation Department
 
// add the given Employee instance to the employees relationship
- (void)addEmployeesObject:(Employee *)anEmployee
{
    NSSet *changedObjects = [[NSSet alloc] initWithObjects:&anEmployee count:1];
    [self willChangeValueForKey:@"employees"
            withSetMutation:NSKeyValueUnionSetMutation
            usingObjects:changedObjects];
    [[self primitiveValueForKey: @"employees"] addObject: anEmployee];
    [self didChangeValueForKey:@"employees"
            withSetMutation:NSKeyValueUnionSetMutation
            usingObjects:changedObjects];
    [changedObjects release];
}
 
// add the given Employee instances to the employees relationship
- (void)addEmployees:(NSSet *)employeesToAdd
{
    [self willChangeValueForKey:@"employees"
            withSetMutation:NSKeyValueUnionSetMutation
            usingObjects:employeesToAdd];
    [[self primitiveValueForKey:@"employees"] unionSet:employeesToAdd];
    [self didChangeValueForKey:@"employees"
            withSetMutation:NSKeyValueUnionSetMutation
            usingObjects:employeesToAdd];
}
 
// remove the given Employee instance from the employees relationship
- (void)removeEmployeesObject:(Employee *)anEmployee
{
    NSSet *changedObjects = [[NSSet alloc] initWithObjects:&anEmployee count:1];
    [self willChangeValueForKey:@"employees"
            withSetMutation:NSKeyValueMinusSetMutation
            usingObjects:changedObjects];
    [[self primitiveValueForKey: @"employees"] removeObject: anEmployee];
    [self didChangeValueForKey:@"employees"
            withSetMutation:NSKeyValueMinusSetMutation
            usingObjects:changedObjects];
    [changedObjects release];
}
 
// remove the given Employee instances from the employees relationship
- (void)removeEmployees:(NSSet *)employeesToRemove
{
    [self willChangeValueForKey:@"employees"
            withSetMutation:NSKeyValueMinusSetMutation
            usingObjects:employeesToRemove];
    [[self primitiveValueForKey:@"employees"] minusSet:employeesToRemove];
    [self didChangeValueForKey:@"employees"
            withSetMutation:NSKeyValueMinusSetMutation
            usingObjects:employeesToRemove];
}
 
// trim the employees relationship to only those Employee instances in employeesToIntersect
- (void)intersectEmployees:(NSSet *)employeesToIntersect
{
    [self willChangeValueForKey:@"employees"
            withSetMutation:NSKeyValueIntersectSetMutation
            usingObjects:employeesToIntersect];
    [[self primitiveValueForKey:@"employees"] intersectSet:employeesToIntersect];
    [self didChangeValueForKey:@"employees"
            withSetMutation:NSKeyValueIntersectSetMutation
            usingObjects:employeesToIntersect];
}
@end

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:. If for an attribute attributeName you implement primitiveAttributeName and setPrimitiveAttributeName:, then Core Data will use these methods in place of primitiveValueForKey:@"attributeName" and setPrimitiveValueForKey:@"attributeName".

Primitive accessor methods are useful if you want custom methods to provide direct access to instance variables for persistent Core Data properties, although typically there should be little reason to implement them. 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.