I’ve been playing around with core data and started writing some methods to query different date ranges of data. My core data model is very simple (Entity named Smoke with one field – timestamp (of type date).
When I execute my code, the proper count gets returned, but I get an autorelease error – I used NSZombies to track it to the below method:
- (NSUInteger)retrieveSmokesForUnit:(NSCalendarUnit)unit
{
NSDate *beginDate = [[NSDate alloc] init];
NSDate *endDate = [[NSDate alloc] init];
[self rangeForUnit:unit containingDate:[NSDate date] startsAt:&beginDate andEndsAt:&endDate];
NSInteger count = [self numberOfSmokes:beginDate toDate:endDate];
[beginDate release];
[endDate release];
return count;
}
So I get the concept – I am releasing the NSDate objects beginDate and endDate too many times – but why does that happen? I thought the rule was when you instantiate with alloc, you use release? I don’t release them explicitly anywhere else in the code, so there must be something going on behind the scenes. If someone could point me in the right direction, that would be great!
Here are the other methods involved, since the issue must be somewhere in these. I assume it has to do with how I’m passing pointers to the dates around?
The initial call, called in the view controller
- (IBAction)cigButtonPressed
{
NSUInteger smokes = [[DataManager sharedDataManager] retrieveSmokesForUnit:NSWeekCalendarUnit];
NSLog(@"Count test = %i", smokes);
}
This calles the method posted a the beginning of the question, which in turn calls:
- (NSUInteger)numberOfSmokes:(NSDate *)beginDate toDate:(NSDate *)endDate {
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Smoke" inManagedObjectContext:self.managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
//Create predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(timeStamp >= %@) AND (timeStamp < %@)", beginDate, endDate];
//Setup request
[request setEntity:entity];
[request setPredicate:predicate];
NSError *error;
NSUInteger smokes = [self.managedObjectContext countForFetchRequest:request error:&error];
NSLog(@"Number of smokes retrieved: %d", smokes);
[request release];
return smokes;
}
Thanks!
Edit – left out a related method:
- (void)rangeForUnit:(NSCalendarUnit)unit containingDate:(NSDate *)currentDate startsAt:(NSDate **)startDate andEndsAt:(NSDate **)endDate {
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[calendar rangeOfUnit:unit startDate:&*startDate interval:0 forDate:currentDate];
*endDate = [calendar dateByAddingComponents:[self offsetComponentOfUnit:unit] toDate:*startDate options:0];
[calendar release];
}
In:
startDateandendDateare output parameters. They are not owned by the caller, hence they should not be released.Then, in:
the following happens:
You create a new
NSDateobject via+alloc, hence you own it.beginDatepoints to this new object;You create a new
NSDateobject via+alloc, hence you own it.endDatepoints to this new object;You send
-rangeUnit:containingDate:startsAt:andEndsAt:, passing the address ofbeginDateandendDateas arguments. Upon return, these two variables point to whatever was placed in them by the method. You do not own the corresponding objects (see above), and you’ve leaked the twoNSDateobjects you created in steps 1 and 2.You send
-releaseto bothbeginDateandendDate. You don’t own them, hence you shouldn’t release them.In summary:
You shouldn’t be creating new objects for
beginDateandendDatesince they’re being returned by-rangeUnit…This causes memory leaks;You shouldn’t be releasing
beginDateandendDatebecause you do not own the objects returned by-rangeUnit…This causes overreleases.The following code should fix your leaks and overreleases: