Question about copy and retain properties


#1

I am particularly grateful for the exercise about adding a subtitle to the MapAnnotation object, as I ran into some weird memory issues and I had to understand and deal with them.

So now I solved the exercise, but I’m left with something that doesn’t feel right.

Here’s my MapPoint header file, where I use copy for the instance variables:

@interface MapPoint : NSObject <MKAnnotation> {
	NSString *title;
	NSString *subtitle;
	CLLocationCoordinate2D coordinate;
}

@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;

- (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t subtitle:(NSString *)st;

And here’s the implementation, where I have to call retain, otherwise I’d get a memory error:

...

- (id)initWithCoordinate:(CLLocationCoordinate2D)c title:(NSString *)t subtitle:(NSString *)st
{
	[super init];
	coordinate = c;
	title = [t retain];
	subtitle = [st retain];
	return self;
}

...

That doesn’t feel right to me. I shouldn’t call retain on a copy property right?

Here’s where I call it from:

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
	NSLog(@"%@", newLocation);
	// How many seconds ago was this location created?
	NSTimeInterval t = [[newLocation timestamp] timeIntervalSinceNow];
	// CLLocation will return the last found location of the
	// device first, you don't want that data in this case.
	// If this location was made more than 3 minutes ago, ignore it.
	if (t < -180) {
		// cached data, you don't want it
		return;
	}
	
	NSString *subtitle = [NSDateFormatter localizedStringFromDate:
						  [newLocation timestamp]
							dateStyle:NSDateFormatterShortStyle
							timeStyle:NSDateFormatterNoStyle];
	
	MapPoint *mp = [[MapPoint alloc]
					initWithCoordinate:[newLocation coordinate] title:[locationTitleField text]  subtitle];
	[mapView addAnnotation:mp];
	[mp release];
	[self foundLocation];
}

I’m sure there’s something wrong with my code, but I can’t understand it right now. Regardless, is calling retain on a copy property a good practice?


#2

When you just use the assignment operator for setting the title and subtitle members of the object in your initWithCoordinate:title:subtitle: method, you bypass the property accessors. I think if you use the following code for setting the members, the accessors will be used instead and a copy of the strings will be taken.

   [self setTitle:t];
   [self setSubtitle:st];

#3

The attributes you set for a property are used when you synthesize that property. You can, but probably shouldn’t, do anything you want in the implementation file despite what it says in the header file - the compiler will be okay with that. However, when you specify the memory management of a property, you are essentially signing a contract with users of that class (yourself included) saying “If you give me one of these, this is what I will do with it.” Remaining consistent with that behavior will help reduce errors in bigger projects, where you simply can’t remember everything that is going on in that project, and must rely on stuff like this to tell you.