Simple approach to the Annotation Challenge


#1

I have seen solutions posted that clutter either the initWithCoordinate:title: method of MapPoint or even the AppDelegate. I came up with a solution that puts the date formatting logic into a custom setSubtitle accessor method, which seems appropriate, since only the subtitle needs to know about the fact that the subtitle has a formatted date. This is an easy, clean solution, because aside from the custom setSubtitle method, only 4 lines of code need to be added to the code from the book.

First, I’ll give the four new lines of code that are added:

  1. In MapPoint.h, declare an NSString *subtitle, just after (and just like was done for) *title.

  2. Also add the @property for subtitle. But make it readonly, because we don’t want the compiler to generate a setSubtitle method; we’re going to write a custom one. Here’s the @property statement:

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

  1. In MapPoint.m, we’re going to generate a subtitle whenever we generate a title, but we’re not going to supply a value (since it will be set automatically to the current date/time). So after the setTitle call add this:

[self setTitle:t]; //from book
[self setSubtitle]; //add this line

  1. Add to dealloc:

[subtitle release];

Don’t forget to add subtitle to your @synthesize statement(s). Remember it will direct the compiler to write the “get” accessor method for subtitle, but not the “set” method since it’s readonly. Now we’ll write setSubtitle, which has the date formatting logic. Add the following method to MapPoint.m:

-(void)setSubtitle
{
NSDate *now = [[NSDate alloc] init];
NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setDateStyle:NSDateFormatterShortStyle];
[df setTimeStyle:NSDateFormatterShortStyle];
NSString *stringNow = [df stringFromDate:now];

[df release];
[now release];

[subtitle release];
[stringNow retain];

subtitle = stringNow;
}

That’s it! I welcome any comments on this (and other) solutions.


#2

I should have mentioned that the code line in step 3 is added to MapPoint’s initWithCoordinate:title: method.


#3

If I can, I’d like to offer a correction and a suggestion.

That’s not a reason to make a property readonly. As the book notes,

[quote=“book page 71”]
You can also synthesize a property and implement one of the accessor methods; this overrides the method you replaced without affecting its partner.
[/quote]You can also think about it this way: if instead you wanted to override the getter, would you declare the property writeonly? :wink:

By convention (and as you’ve seen, particularly with the rules for memory management, conventions are important), the -set… methods expect a parameter. Having a set without a parameter just feels “wrong”. I cannot say whether there is anything bad that would come of it, but at the very least it would cause confusion for later developers.

As an alternative, consider letting the setter be generated without changes (and declare the @property without the readonly attribute), but invoke it a little differently.

@interface MapPoint (PrivateMethods)
- (NSString *) formattedDate;
@end

@implementation MapPoint
...
[self setTitle:t];
[self setSubtitle: [self formattedDate]];
...
- (NSString *) formattedDate
{
    NSDate *now = [[NSDate alloc] init];
    NSDateFormatter *df = [[NSDateFormatter alloc] init];

    [df setDateStyle:NSDateFormatterShortStyle];
    [df setTimeStyle:NSDateFormatterShortStyle];
    NSString *stringNow = [df stringFromDate:now];

    [df release];
    [now release];

    return stringNow;
}

#4

Thanks for your comments and good suggestion!

Philisophically, I can see why someone might feel that a setter without a parameter would feel “wrong.” But to me, I see no problem with an attribute that “knows” how to determine its value when it is set. A comment in the header file could explain this “auto-set without a parameter” aspect to future developers. But such a comment would not be needed with your approach because it avoids this aspect of my code that feels “wrong” to some developers.

As for the readonly property, my understanding is that it only affects the accessors that the compiler generates. It does not imply (to me) that write access will be forbidden. But my understanding of Apple’s intent here is not very deep. With this shaky understanding, I’ve wondered why not writeonly? :neutral_face: But as you point out In the quote from the book, my custom setter overrides any the compiler would have otherwise generated, and so readonly is not required.

Thanks again!


#5

Hello,

Just finished this chapter.

If a @property was set to “writeonly”. How would you be able to get its value? Isn’t this the reason “writeonly” is not an available option?

Eric.


#6

That’s actually why I put that out there. :slight_smile:

That said, the property attributes serve two purposes: they tell @synthesize how to create the getter and (optionally) the setter, and (the relevant part here) they describe for the human reader what’s publicly available for the class. So the theoretical (since as you noted it doesn’t exist) writeonly attribute would tell the user they could only set the property but the class implementation could still read it (e.g., as a config value).

I was just trying to provide a means of remembering that it’s not necessary (or even desired) to specify readonly when you want to override the setter. If the mention of writeonly causes confusion, then please feel free to ignore it.