Neigaard
Neigaard

Reputation: 4050

Why does this Objective C code leak memory?

UPDATED

I have this method in Objective C:

-(NSDate*)roundTo15:(NSDate*)dateToRound {
    int intervalInMinute = 15;
    // Create a NSDate object and a NSDateComponets object for us to use
    NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSMinuteCalendarUnit fromDate:dateToRound];

    // Extract the number of minutes and find the remainder when divided the time interval
    NSInteger remainder = [dateComponents minute] % intervalInMinute; 
    // gives us the remainder when divided by interval (for example, 25 would be 0, but 23 would give a remainder of 3

    // Round to the nearest 5 minutes (ignoring seconds)
    if (remainder >= intervalInMinute/2) {
        dateToRound = [dateToRound dateByAddingTimeInterval:((intervalInMinute - remainder) * 60)]; // Add the difference
    } else if (remainder > 0 && remainder < intervalInMinute/2) {
        dateToRound = [dateToRound dateByAddingTimeInterval:(remainder * -60)]; // Subtract the difference
    }

    return dateToRound;
}

This is how I call the method:

item.timestamp = 
    [self roundTo15:[[NSDate date] dateByAddingTimeInterval:60 * 60]];

Instruments says I am leaking a NSDate object when the following line is executed:

dateToRound = [dateToRound dateByAddingTimeInterval:(remainder * -60)];

So it is my item object I need to update with a new corrected NSDate. I tried by making a roundedDate and returning it like this: return [roundedDate autorelease];, but then I got a bad access error.

Upvotes: 1

Views: 566

Answers (2)

Kris Markel
Kris Markel

Reputation: 12112

The problem is that dateToRound is being passed in as a reference to one object and you are setting it to a reference to a different object. The original object is now abandoned and has been leaked.

You should create a new NSDate * and return it instead of reassigning dateToRound.

Sample code:

-(NSDate*)roundTo15:(NSDate*)dateToRound {
    int intervalInMinute = 15;
    // Create a NSDate object and a NSDateComponets object for us to use
    NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSMinuteCalendarUnit fromDate:dateToRound];

    // Extract the number of minutes and find the remainder when divided the time interval
    NSInteger remainder = [dateComponents minute] % intervalInMinute; // gives us the remainder when divided by interval (for example, 25 would be 0, but 23 would give a remainder of 3

    // Round to the nearest 5 minutes (ignoring seconds)
    NSDate *roundedDate = nil;
    if (remainder >= intervalInMinute/2) {
        roundedDate = [dateToRound dateByAddingTimeInterval:((intervalInMinute - remainder) * 60)]; // Add the difference
    } else if (remainder > 0 && remainder < intervalInMinute/2) {
        roundedDate = [dateToRound dateByAddingTimeInterval:(remainder * -60)]; // Subtract the difference
    } else {
        roundedDate = [[dateToRound copy] autorelease];
    }

    return roundedDate;
}

Upvotes: 3

Jason McCreary
Jason McCreary

Reputation: 72991

Some class methods may return a new object on your behalf. Check the documentation, but my guess is that dateByAddingTimeInterval does. That is to say the object returned is not set as autorelease. In which case you would need to release it yourself.

I have found Instruments to report some things that aren't that intuitive. Don't get me wrong, it's a great tool and awesome that you are using. But even some of the sample code from Apple reports leaks.

Upvotes: -2

Related Questions