Search code examples
objective-ccore-datamemory-managementnsfetchedresultscontrollerdealloc

Taking ownership of autorelease objects by using retain?


What is the proper way to approach the following. To obtain a list with CoreData objects I have a 'datamanager' in which I create a NSFetchResultController, do a search, and result the NSFetchResultsController with the objects.

Because there can be multiple tables, on each request a new FetchedResultsController needs to be created in order to allow automatic updates of the tableViews they are associated with.

What I do is create FetchedResultController in the DataManager, and autorelease it. In the TableViewController I retain it (assuming that it would take ownership of it).

    NSFetchedResultsController* objects = [[delegate dataManager] getMenu:parentMenu];        
    _objects = [objects retain];

This calls the DataManager:

- (NSFetchedResultsController*) getMenu:(DOMenuItem*) parentMenu {
    NSPredicate* predicate;
    if(parentMenu == nil){
        predicate = [NSPredicate predicateWithFormat:@"(parent == NULL)", parentMenu];
    } else {     
        predicate = [NSPredicate predicateWithFormat:@"(parent == %@)", parentMenu];    
    }

    [NSFetchedResultsController deleteCacheWithName:_cacheName];

    NSFetchedResultsController* aFetchedResultsController = [self createFetchedResultsController];    
    [aFetchedResultsController.fetchRequest setPredicate:predicate];
    [aFetchedResultsController.fetchRequest setFetchLimit:0];

    NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:_defaultSortField ascending:YES] autorelease];
    NSArray *sortDescriptors = [[[NSArray alloc] initWithObjects:sortDescriptor, nil] autorelease];

    [aFetchedResultsController.fetchRequest setSortDescriptors:sortDescriptors];        

    // Fetch the records and handle an error
    NSError *fetchError;

    if (![aFetchedResultsController performFetch:&fetchError]) {
        // Handle the error.
        // This is a serious error and should advise the user to restart the application
        NSLog(@"Fetching data error: %@", [fetchError localizedDescription]);
    }

    return aFetchedResultsController;    
} 

- (NSFetchedResultsController *) createFetchedResultsController {     
    NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
    NSEntityDescription *entity = [NSEntityDescription entityForName:_objectName inManagedObjectContext:[self managedObjectContext]];
    [fetchRequest setEntity:entity];

    NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:_defaultSortField ascending:NO] autorelease];
    NSArray *sortDescriptors = [[[NSArray alloc] initWithObjects:sortDescriptor, nil] autorelease];

    [fetchRequest setSortDescriptors:sortDescriptors];

    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[self managedObjectContext] sectionNameKeyPath:_sectionNameKeyPath cacheName:_cacheName];

    return [aFetchedResultsController autorelease];
} 

However, I am running in situations where I get a Dealloc error on this FetchedResultController on a call on the line.

if(indexPath.section == [[_objects sections] count] + 1){

The message is:

* -[NSFetchedResultsController sections]: message sent to deallocated instance 0xe3de1b0

Is there something wrong with this method? If so, what would be the preferred way to 'transfer' ownership from the creating class to a class which needs to retain it.

Edit: When I put a 'retain' instead of an 'autorelease' when returning the fetchedResultsController, then it all works fine, but that doesn't seem proper coding practice as the 'owner' doesn't release the object. I don't think 'copy' or so works with ManagedObjects or does it?


Solution

  • The proper way, in a non-ARC environment, is to return the object autoreleased, then, if the use of the object will persist beyond the scope of the receiving procedure (and, recursively, the procedure that called it, back to the closest autorelease boundary), the object should be retained, with provision made (usually in the "owning" object's dealloc method) to release the retain on a 1-for-1 basis.

    Note that one needs to be careful in situations where the object may be conditionally retained -- don't release what you didn't retain.