@synthesize
directive that tells the compiler to write getters and setters automatically.We already saw that the
@property
directive can shrink the boilerplate code in a class @interface
declaration somewhat. We started with this:@interface Person : NSObject { int age; Address *address; NSString *name; } - (int)age; - (void)setAge:(int)anAge; - (Address *)address; - (void)setAddress:(Address *)anAddress; - (NSString *)name; - (void)setName:(NSString *)aName; // ... @endAnd by using properties, we end up with this:
@interface Person : NSObject { int age; Address *address; NSString *name; } @property int age; @property (retain) Address *address; @property (copy) NSString *name; // ... @endIt's not a giant difference, but it's an improvement. As well as making the code more compact, it separates the logical properties of the class from the rest of the methods. Where
@property
really starts to pay off is when we combine it with @synthesize
. The @implementation
for the Person
class looks like this:// hand written getter and setter methods @implementation Person - (int)age { return age; } // assign-type setter - (void)setAge:(int)anAge { age = anAge; } - (Address *)address { return address; } // retain-type setter - (void)setAddress:(Address *)anAddress { if (address != anAddress) { [address release]; address = [anAddress retain]; } } - (NSString *)name { return name; } // copy-type setter - (void)setName:(NSString *)aName { if (name != aName) { [name release]; name = [aName copy]; } } // ... @endThat's a lot of boilerplate code to churn out, and memory management in the retain and copy type setters makes it more error prone than simple assign type setters. It's just begging to be machine generated, so let's do that:
// synthesized getter and setter methods @implementation Person @synthesize age, address, name; // ... @endAll that onerous code can be deleted, and the compiler now generates the correct type of getters and setters based on the attributes of the corresponding
@property
directive. Note that to use @synthesize
, you must have a corresponding @property
in the @interface
section. I like to put the property names in a list after @synthesize
, but you can have multiple @synthesize
lines if you like, with one or several property names per line:@implementation Person @synthesize age; @synthesize address, name; // ... @endIf you need to provide a particular getter or setter yourself, but you want the compiler to write the rest of them, you simply add it to your
@implementation
. The compiler looks first to see what you provided before it generates anything. So if we need to do a check when setting the age
of a Person
instance, we simply write our own setter:// synthesized getter and setter methods // with one custom setter @implementation Person @synthesize age, address, name; - (void)setAge:(int)anAge { age = anAge; if (age >= 55) { [[JunkMailer sharedJunkMailer] addPersonToAARPMailingList:self]; } } // ... @endThis is great, but sometimes you want the property to have a different name than the instance variable that backs it up. For example, we might call the instance variable
ageInYears
, but want to call the property age
:@interface Person : NSObject { int ageInYears; // ... } @property int age; // ... @end @implementation Person @synthesize age, address, name; // ... @endThis confuses the compiler and it complains:
synthesized property 'age' must either be named the same as a compatible ivar or must explicitly name an ivarSo how do we tell it to use the instance variable
ageInYears
when generating the age
property? The @synthesize
directive has one modifier, which solves our problem:@implementation Person @synthesize age = ageInYears, address, name; // ... @end
That wraps up normal usage of
@synthesize
. Next week, we look at one attribute of @property
that we've neglected so far: nonatomic
.
8 comments:
Thanks for doing the Objective-C Tuesdays series. I have read a lot the Apple Programming Guides and have created an iPhone app for my company, but because of how I have thrown myself in to the language to quickly learn and get an app off and running there are certain pieces that I miss - such as @property and synthesize. I use these all the time, and understood the end result but didn't realize the separation till reading this article. Thanks again.
Thanks Cooper! I think most iPhone developers dove into Objective-C and Cocoa Touch head first (I know I did when I started in 2008) and have to backfill their knowledge of the language as they move forward.
Hm, I still don't understand the benefit of naming a property differently than the instance variable it represents. Could you give an example where it makes sense to do so? Thanks.
Personally, I prefer the property and instance variable names to be the same. The most common example of changing the property name might be for a BOOL instance variable such as "active". Some coders would prefer to see code like "if (myobject.isActive) { ... }" instead of "if (myobject.active) { ... }". I don't think that "isActive" is a big improvement over "active", but some people prefer it. I'd rather have the naming pattern be consistent without any special cases.
Hi Don,
This is a great series! Thanks for the good work! I have a question regarding @synthesize. I have noticed that - contrary to what I'm reading here - Objective C seems to also generate the ivar for a property if it's not there in the @interface.
Have things changed since your post or did I get something wrong?
Thanks,
Fed
Thanks Fed!
Apple created a "modern runtime" for Objective-C several years ago that will automatically generate instance variables. The modern runtime has been available for 64-bit OS X apps for a while, and is available on iOS starting with 4.0. If your iOS deployment target is 4.0 or later, then you can use this feature. If you're still targeting older versions of iOS, you'll need to explicitly declare all your instance variables.
Given that most iOS users upgrade their OS pretty rapidly, at this date, most users are running iOS 4.0 or later. We will probably change our iOS deployment target to 4.0 later this year and be able to take advantage of all the nice 4.0 features.
Thank you Don, it makes perfect sense. Now you mention I remember finding that this feature had an erratic behaviour (sometimes it wouldn't work). I have a few projects that are still on iOS 3.0, so most likely that's why I was finding inconsistent behaviour. Do you know by any chance if the ivar that is injected into the struct is protected or private or whatever and whether there is a way of influencing that? It's not vital, don't go looking for an answer if you don't know off the top of your head ;-)
And again my congrats for this great blog!
I think the created ivars are private by default, but I don't recall for sure if I read that and I don't remember there being any way to change the visibility. I think it's assumed that if you're creating the properties, you want to hide the ivar.
Post a Comment