views:

48

answers:

2

I have been slowly learning iPhone development and seem to keep hitting walls where I can't figure out how to do what I want to do the right way :(

Basically, I want a class that handles all interactions with the data layer, for example, getting a mutable array of some list of objects from the data store.

This is pretty trivial in other languages where you have a garbage collector, but in Objective-C on the iPhone, I'm not sure what to do.

This is an example method on a DataFactory class we were creating. Note the comment on where we are not sure when to release....

- (NSMutableArray*)fetchAllDrivers{

    NSMutableArray *results = [[NSMutableArray alloc] init];;

    if (self.appDelegate != nil) {
        NSManagedObjectContext *context = [self.appDelegate managedObjectContext];

        NSFetchRequest *request = [[NSFetchRequest alloc] init];
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:context];
        [request setEntity: entity];

        NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastName" ascending:NO];
        NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: sortDescriptor, nil];
        [request setSortDescriptors: sortDescriptors];
        [sortDescriptors release];
        [sortDescriptor release];

        NSError *error;
        results = [[context executeFetchRequest:request error:&error] mutableCopy];
        if (results == nil) {
            //something went wrong
        }

        //Where should this be released???  Certainly not here!
        [results release];

        [request release];
    }
    else {
        [NSException raise:@"Can't fetch b/c app delgate is nil!" format: @"!!!"];    
    }

    return results;
}

Calling code, related to my comment:

NSMutableArray* arr = [dataFactory fetchAllDrivers];
[arr retain];
//Some code  where we use arr
[arr release];
+1  A: 

Following naming conventions, your fetchAllDrivers should return an autoreleased object.

- (NSMutableArray*)fetchAllDrivers
{

    if (!self.appDelegate) {
        // Big Problems Raise exception immediately if you want...
        return nil; 
    }

    NSManagedObjectContext *context = [self.appDelegate managedObjectContext];

    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:context];
    [request setEntity: entity];

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastName" ascending:NO];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: sortDescriptor, nil];
    [request setSortDescriptors: sortDescriptors];
    [sortDescriptors release];
    [sortDescriptor release];


    NSError *error = nil;

    NSMutableArray *results = [[NSMutableArray alloc] initWithArray:[context executeFetchRequest:request error:&error] copyItems:YES];

    if (error) {
        // Something went wrong
        [results release];
        // Error handling code here
        [request release];
        return nil;
    }

    [request release];
    return [results autorelease];

}
falconcreek
Ah, I was wondering when to use autorelease! So if I remember my memory management learning correctly, the calling code will need to stake ownership in the returned object right? (see code I added at the bottom of my question)
spilliton
You've got the groove now. You should check that arr is not nil though.
falconcreek
+1  A: 
NSMutableArray* arr = [dataFactory fetchAllDrivers];
[arr retain];
//Some code  where we use arr
[arr release];

By convention, any object returned from the method of an external object is autoreleased. You don't need to retain them except in properties. If you only using arr in the local scope of the method then you don't need to retain/release it. It is autoreleased and will die after the end of the local scope.

If you need to have arr hang around inside the object. You should store it in a retained property:

@property (nonatomic,retain) NSMutableArray *arr;

... then use it with the self notation to ensure retention:

self.arr=[dataFactory fetchAllDrivers];

... then you need only release it in the class' dealloc method.

Having one object manage your data model is very good idea but it is not a "factory". Objective-c does not use factories like C++ and similar languages. Trying to think in those terms will lead to grief. The object should instead be thought of as a "controller" or "manager".

TechZen
We will in fact be storing arr in a property (that is the data source for a table), so thanks for the explanation! Also, the tidbit about not having to retain in the scope of a method when you receive an autorelease object, that makes sense too. Thanks!
spilliton