Challenge: Annotations


#1

I have come up with this for the first Challenge at the end of Chapter 5:

[code]@implementation MapPoint

@synthesize coordinate, title;

  • (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t
    {
    self = [super init];
    if (self)
    {
    coordinate = c;

      NSDateFormatter *dateFormat = [[[NSDateFormatter alloc] init] autorelease];
      [dateFormat setDateFormat:@" dd-MM-yyyy"];
      NSString *theDate = [dateFormat stringFromDate:[[[NSDate alloc] init] autorelease]];
      
      NSString *titleNameAndDate = [NSString stringWithFormat:@"%@\r\n%@",theDate, t];
    
      [self setTitle:titleNameAndDate];
    

    }
    return self;
    }

  • (void)dealloc
    {
    [title release];
    [super dealloc];
    }

@end[/code]

When I did Build and Analyze I had potential memory leaks at:
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
and
NSString *theDate = [dateFormat stringFromDate:[[NSDate alloc] init]];
so I added autorelease to the end of the statements.

From other material it has been noted that using autorelease all the time is not a good idea, so is there any better way I should be releasing these objects?

Cheers,
Nick
http://myfirstiphoneapp.co.uk


#2

Hey Nick,

Why not just release the objects after assigning the string using setTitle. You don’t need them afterwards anyways.


#3

You might want to go back and review the rules for memory management.

If you get an object via new, alloc, or copy, then YOU own the object and you are responsible for releasing it, either with -release or with -autorelease. If you get an object any other way (e.g., via +stringWithFormat:) then you must assume it has ALREADY been autoreleased.

You’ll have nothing but trouble if you don’t follow the rules. Adding an extra autorelease will make failure that much harder to track down because the crash won’t be in your code.


#4

[quote=“gc3182”]You might want to go back and review the rules for memory management.

If you get an object via new, alloc, or copy, then YOU own the object and you are responsible for releasing it, either with -release or with -autorelease. If you get an object any other way (e.g., via +stringWithFormat:) then you must assume it has ALREADY been autoreleased.

You’ll have nothing but trouble if you don’t follow the rules. Adding an extra autorelease will make failure that much harder to track down because the crash won’t be in your code.[/quote]

No I understand that perfectly. But Nick uses autorelease on his objects, fine. But he asked if there was a better way, I was wondering why he couldn’t just do this:

// Could also potentially release the objects here, after creating an NSString object with them.

[self setTitle:titleNameAndDate];

// Wouldn't this be acceptable, as we do not need the references to these objects anymore?
[dateFormat release];
[theDate release];

[color=#BF0000]Please correct me if I’m wrong, I would like to know that my understanding of memory management isn’t flawed.[/color]


#5

Sorry; my mistake for not being clear. The “You” I was referring to was Nick, not you. My apologies for that.

But I’m afraid that no, the code you posted would not work properly either.

Let’s go back first to the original code (unformatted, under the ‘code’ box):

NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
NSString *theDate = [dateFormat stringFromDate:[[NSDate alloc] init]];

The first statement is fine (so long as we release it later, or use -autorelease), but the second has a leak we cannot recover from. That’s because it gets an object via +alloc ([NSDate alloc]), so we’re responsible for releasing it…but then it LOSES the reference by building a string from it immediately, not saving the address! Boom; instant memory leak. So we know that’s out.

The quoted code doesn’t fare any better; it has the same issue with losing the reference to an object that we own (because we got it via +alloc). And in your example, we can’t use [theDate release] because theDate was obtained via -stringFromDate. Since this is not new, alloc, or copy, we must assume the reference in theDate is already autoreleased. We can’t release objects that we don’t own without significant risk of the app crashing some time later (which means it would be a bear to debug).

There are as many solutions as there are programmers, but let’s start with this:

NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat...];
NSString *theDate = [dateFormat stringFromDate: [NSDate date]];

The reference in dateFormat was obtained via new, alloc, or copy, so we own it; we must be sure either to release it later or to autorelease it now.

The reference in theDate was NOT obtained via new, alloc, or copy, so we do NOT own it. If we need to use it later, we need to retain it. Since we do not need it later, we don’t need to worry about it. And the NSDate that was used in building the string also was NOT obtained via new, alloc, or copy so we don’t own that; it’s already autoreleased, so again no need to worry.

Setting titleNameAndDate and -setTitle: are fine, so all that’s left is cleanup. Since everything else is already autoreleased, the only thing left is dateFormat. So either add [dateFormat release], or add autorelease to the definition above.

Just remember: if you use new, alloc, or copy, you own the object and are responsible for releasing it. If you obtain a reference by any other means, it’s already autoreleased and you don’t own it. (If you want to own it, then fine, retain it…but you’ll need to release it later as well.)

EDIT: changed new, alloc, or copy to bold to emphasize the recurring theme.


#6

Thanks a lot, I understand the problem perfectly now. Makes a lot of sense.


#7

Thank you gc3182 for the explanation. It helped me as well.