Objective-c – Sample Cocoa app, ref counting, [pool drain] yields EXC_BAD_ACCESS

cocoaobjective-cxcode

I'm working through Cocoa Programming for Mac OS X (3rd ed) and in chapter 4 I wrote this app:

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    //create the date object
    NSCalendarDate *now = [[NSCalendarDate alloc] init];

    //seed random # generator
    srandom(time(NULL));

    NSMutableArray *array;
    array = [[NSMutableArray alloc] init];
    int i;
    for (i=0; i<10; i++){
        //create a date/time that is 'i' weeks from now
        NSCalendarDate *iWeeksFromNow;
        iWeeksFromNow = [now dateByAddingYears:0
                                        months:0
                                          days:(i*7)
                                         hours:0
                                       minutes:0
                                       seconds:0];

        //create a new instance of lottery entry
        LotteryEntry *entry = [[LotteryEntry alloc] init];
        [entry setEntryDate:iWeeksFromNow];

        [array addObject:entry];
        [entry release];
    }
    [now release];
    now = nil;

    for (LotteryEntry *entryToPrint in array) {
        NSLog(@"%@", entryToPrint);
    }
    [array release];
    array = nil;

    NSLog(@"about to drain the pool... (%@)", pool);
    [pool drain];
    NSLog(@"done");
    NSLog(@"GC = %@", [NSGarbageCollector defaultCollector]);
    return 0;
}

The LotteryEntry class looks like this:

@implementation LotteryEntry

- (void)setEntryDate:(NSCalendarDate *)date
{
    entryDate = date;
}

- (NSCalendarDate *)entryDate
{
    return entryDate;
}

- (int)firstNumber
{
    return firstNumber;
}

- (int)secondNumber
{
    return secondNumber;
}

- (id)init
{
    return [self initWithDate:[NSCalendarDate calendarDate]];
}

- (id)initWithDate:(NSCalendarDate *)date
{
    if(![super init])
        return nil;

    NSAssert(date != nil, @"Argument must be non-nil");

    firstNumber = random() % 100 + 1;
    secondNumber = random() % 100 + 1;
    entryDate = [date retain];  
    return self;
}

- (NSString *)description
{
    NSString *result;
    result = [[NSString alloc] initWithFormat:@"%@ = %d and %d",
              [entryDate descriptionWithCalendarFormat:@"%b %d %Y"],
              firstNumber,
              secondNumber];
    return result;
}

- (void)dealloc
{
    NSLog(@"deallocating %@", self);
    [entryDate release];
    [super dealloc];
}

@end

As you can see I'm retaining and releasing the objects here. I'm pretty sure my code matches the book's, however when I run the app, at the [pool drain] I get this message:

Program received signal:
“EXC_BAD_ACCESS”.

I'm not sure what's causing this. I'm hoping it's something stupid that I missed, but I'd sure appreciate a few other pairs of eyes on it. Thanks in advance!

(side note: I'm a .NET developer, so ref counting is pretty foreign to me!)

Best Solution

It also looks like you have a bug in this method:

- (id)initWithDate:(NSCalendarDate *)date
{
    if(![super init])
        return nil;

    NSAssert(date != nil, @"Argument must be non-nil");

    firstNumber = random() % 100 + 1;
    secondNumber = random() % 100 + 1;
    entryDate = [date retain];  
    return self;
}

You are essentially discarding the results from [super init], while it may not be a problem in this instance, it could cause serious problems in others. You should 'always' structure you init methods like this:

- (id)initWithDate:(NSCalendarDate *)date
{
    if(self = [super init]) {
        NSAssert(date != nil, @"Argument must be non-nil");

        firstNumber = random() % 100 + 1;
        secondNumber = random() % 100 + 1;
        entryDate = [date retain];
}
return self;

If you are not going to return self from an init method (for instance it is a factory or something odd like that), you should remember to release self. It has been alloc'ed, and if you don't return it, it cannot be released properly. Example:

- (id) init
{
    NSObject*    newSelf = [[NSObject alloc] init];

    [self release];
    return newSelf;
}
Related Question