+2  A: 
Ben Gottlieb
I'd say don't use the dot-notation, if he implements custom accessors in the future and they depend on the state of the object being sane, they may not work in the initialiser. It's safest there (and -dealloc) to stick with direct ivar banging.
Graham Lee
I agree, but some people are religious about access via properties.
Ben Gottlieb
Thanks everyone! It works great now.
A: 

Slightly off topic - Ben has the answer you are looking for but the logic in some of your code is a little tortuous.

- (void)setName:(NSString *)aString {
    if ((!name && !aString) || (name && aString && [name isEqualToString:aString])) return;
    [name release];
    name = [aString copy];
}

You don't really need to check (!name && !aString) it's safe and common practice to reassign nil over nil and to send a release message (or any message) to nil. The runtime short circuits the latter and the optimiser will remove the former. If setting a nil name is an error, you should assert that aString is not nil

Similarly, NSStrings are very well optimied. Don't bother optimising out the case where you are setting the name of a car to the name that is already set. Infact, your check [name isEqualToString:aString] which you take every time someone set a non-nil string is slower than the release and copy you are avoiding in the minority of cases.

If you check what happens when you send copy to an NSString, you'll find it just increases the retain count. Note that this is not true for NSMutableString for obvious reasons ;-)

A good rule of thumb - optimise when you have all functionality and only optimise what is slow (or big).

finally, following on from Ben's comments about naming, I'd rename aString to aCarName or something else more descriptive - again, your future self will thank you.

Roger Nolan