Fetching Managed Objects

This article describes how to fetch managed objects and discusses some considerations for ensuring that fetching is efficient. It also shows how you can use NSExpressionDescription objects to retrieve particular values. For code snippets that you can use for various scenarios, see Core Data Snippets.

Fetching Managed Objects

You fetch managed objects by sending a fetch request to a managed object context. You first create a fetch request. As a minimum you must specify an entity for the request. You can get the entity from your managed object model using the NSEntityDescription method entityForName:inManagedObjectContext:. You may also set a predicate (for details about creating predicates, see Predicate Programming Guide), sort descriptors, and other attributes if necessary. You retrieve objects from the context using executeFetchRequest:error:, as illustrated in the example below.

Listing 1  Example of creating and executing a fetch request

NSManagedObjectContext *moc = [self managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription
    entityForName:@"Employee" inManagedObjectContext:moc];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
 
// Set example predicate and sort orderings...
NSNumber *minimumSalary = ...;
NSPredicate *predicate = [NSPredicate predicateWithFormat:
    @"(lastName LIKE[c] 'Worsley') AND (salary > %@)", minimumSalary];
[request setPredicate:predicate];
 
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
    initWithKey:@"firstName" ascending:YES];
[request setSortDescriptors:@[sortDescriptor]];
 
NSError *error;
NSArray *array = [moc executeFetchRequest:request error:&error];
if (array == nil)
{
    // Deal with error...
}

You cannot fetch using a predicate based on transient properties (although you can use transient properties to filter in memory yourself). Moreover, there are some interactions between fetching and the type of store—for details, see “Store Types and Behaviors.” To summarize, though, if you execute a fetch directly, you should typically not add Objective-C-based predicates or sort descriptors to the fetch request. Instead you should apply these to the results of the fetch. If you use an array controller, you may need to subclass NSArrayController so you can have it not pass the sort descriptors to the persistent store and instead do the sorting after your data has been fetched.

If you use multiple persistence stacks in your application, or if multiple applications might access (and modify) the same store simultaneously, you can perform fetches to ensure that data values are current—see “Ensuring Data Is Up-to-Date.”

Retrieving Specific Objects

If your application uses multiple contexts and you want to test whether an object has been deleted from a persistent store, you can create a fetch request with a predicate of the form self == %@. The object you pass in as the variable can be either a managed object or a managed object ID, as in the following example:

NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity =
    [NSEntityDescription entityForName:@"Employee"
            inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
 
NSPredicate *predicate =
    [NSPredicate predicateWithFormat:@"self == %@", targetObject];
[request setPredicate:predicate];
 
NSError *error;
NSArray *array = [managedObjectContext executeFetchRequest:request error:&error];
if (array != nil) {
    NSUInteger count = [array count]; // May be 0 if the object has been deleted.
    //
}
else {
    // Deal with error.
}

The count of the array returned from the fetch will be 0 if the target object has been deleted. If you need to test for the existence of several objects, it is more efficient to use the IN operator than it is to execute multiple fetches for individual objects, for example:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self IN %@",
                                            arrayOfManagedObjectIDs];

Fetching Specific Values

Sometimes you don’t want to fetch actual managed objects; instead, you just want to retrieve—for example—the largest or smallest value of a particular attribute. In OS X v10.6 and later and on iOS, you can use NSExpressionDescription to directly retrieve values that meet your criteria.

You create a fetch request object and set its entity, just as you would for a normal fetch, but:

There are a number of steps to follow to create and use the expression description.

  1. First you need to create expressions (instances of NSExpression) to represent the key-path for the value you’re interested in, and to represent the function you want to apply (such as max: or min:):

    NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:@"salary"];
    NSExpression *maxSalaryExpression = [NSExpression expressionForFunction:@"max:"
                                                      arguments:[NSArray arrayWithObject:keyPathExpression]];

    For a full list of supported functions, see expressionForFunction:arguments:.

  2. You then create the expression description and set its name, expression, and result type.

    The name is the key that will be used in the dictionary for the return value. If you want to retrieve multiple values—such as the largest and the smallest salaries in an Employee table—the name of each expression description must be unique for a given fetch request.

    NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
    [expressionDescription setName:@"maxSalary"];
    [expressionDescription setExpression:maxSalaryExpression];
    [expressionDescription setExpressionResultType:NSDecimalAttributeType];
  3. Finally, you set the request’s properties to fetch just the property represented by the expression:

    [request setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];

You can then execute the fetch request just as you would any other (using executeFetchRequest:error:). The request returns, though an array containing a dictionary whose keys and values correspond to the names of the expression descriptions and the values you requested.

The following example illustrates how you can get the minimum value of an attribute “creationDate” in an entity named “Event”.

NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:context];
[request setEntity:entity];
 
// Specify that the request should return dictionaries.
[request setResultType:NSDictionaryResultType];
 
// Create an expression for the key path.
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:@"creationDate"];
 
// Create an expression to represent the minimum value at the key path 'creationDate'
NSExpression *minExpression = [NSExpression expressionForFunction:@"min:" arguments:[NSArray arrayWithObject:keyPathExpression]];
 
// Create an expression description using the minExpression and returning a date.
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
 
// The name is the key that will be used in the dictionary for the return value.
[expressionDescription setName:@"minDate"];
[expressionDescription setExpression:minExpression];
[expressionDescription setExpressionResultType:NSDateAttributeType];
 
// Set the request's properties to fetch just the property represented by the expressions.
[request setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];
 
// Execute the fetch.
NSError *error = nil;
NSArray *objects = [managedObjectContext executeFetchRequest:request error:&error];
if (objects == nil) {
    // Handle the error.
}
else {
    if ([objects count] > 0) {
        NSLog(@"Minimum date: %@", [[objects objectAtIndex:0] valueForKey:@"minDate"]);
    }
}

Fetching and Entity Inheritance

If you define an entity inheritance hierarchy (see “Entity Inheritance”), when you specify a super-entity as the entity for a fetch request, the request returns all matching instances of the super-entity and of sub-entities. In some applications, you might specify a super-entity as being abstract (see “Abstract Entities”). To fetch matching instances of all concrete sub-entities of the abstract entity, you set the entity for fetch specification to be the abstract entity. In the case of the domain described in “Abstract Entities,” if you specify a fetch request with the Graphic entity, the fetch returns matching instances of Circle, TextArea, and Line.