Now for my next rant on poor Objective-C code. Coming from other languages it's understandable that most developers don't take full advantage of the power of Objective-C 2.0. Here's a practice I hope that becomes more widespread.
One feature of Objective-C that is underutilized is @property. The Objective-C compiler can write so much code for us, we just have to let it. Note that some of what I'm going to cover only works on the 64-bit (modern) Objective-C runtime. However, you probably won't find any situation where you need to write code for the 32-bit (legacy) runtime. The only reason we wrote legacy runtime code was because that’s what the iOS simulator used to run on (prior to iOS 4.0). That was fixed over a year ago.
Clean up your Headers
First, let's use @property to clean up our header files. The first thing to do is delete all of your ivars. Yes, delete all of them. They are no longer necessary. @synthesize will not only make your getters and setters, but it will also make your ivars. This was one of the major changes from the legacy to modern runtimes. This is nice because we don't need to be redundant about our instance variables anymore. Now, for all of your public ivars, make an equivalent @property. We'll handle the private and protected ivars in a moment. With just your public @propertys, your headers will now be much shorter and easier to read. Remember, headers are for public information. That is the whole idea behind encapsulation. You don't want your private information in your public headers. Even though I'm focusing on @propertys, you can apply the same ideas to methods too.
Categories and Class Extensions
Before handling your private and protected ivars, you need to understand categories and class extensions. If you feel comfortable using class extensions, feel free to jump to the next section.
A category is a way to extend the interface and functionality of an existing class. You declare them similarly to regular classes:
@interface MyClass (CategoryName)
// Extra method declarations.
@implementation MyClass (CategoryName)
// Extra method implementations.
Making a category is that simple. You just need to remember to #import your category when you want to use your extra methods. Class extensions, for all intents and purposes, are categories. The main differences are that only the original class can create a class extension for itself and you can declare @propertys in a class extension. So we will do just that. To create a class extension you simply create a category with no name. It will look something like this:
@interface MyClass ()
@property (nonatomic, copy) NSString * myPrivateString;
With your class extension in place, you just need an associated @synthesize myPrivateString; and you're done. It's that simple. You can do this with all of your private class variables with no problems.
Now we are left with the protected variables. These are a little more tricky. Technically protected variables and methods don't exist with this technique. Then again “private” technically doesn't exist at all in Objective-C. The runtime won't stop you from calling private methods. Anyway, you should avoid protected data as often as possible. Sometimes that just isn't possible though. If you want a protected @property, just put it in your class extension like your private data. Then, any subclasses that need to access the protected property need to contain an identical @property in their class extensions. You will want to include some documentation about your protected @property. This does seem a little redundant to have identical @propertys, but it's bearable since Objective-C generates the important code for you. Again, this also works for methods. This is particularly nice because the @protected key word doesn't work on methods like it does ivars. One thing to note is that you can't call super in a protected method using this technique (you will get an annoying warning). If you need to make a super call, then your protected method should be placed in a private header instead. This private header should contain just the prototypes and is ONLY imported by classes that need that information.
What about when you want to make a public getter but a private setter? Objective-C can do this for you too. In your public header you can do something like this:
@property (nonatomic, copy, readonly) NSString * myPrivateString;
Then in your class extension you make a similar declaration:
@property (nonatomic, copy, readwrite) NSString * myPrivateString;
You just need to make sure the two declarations mirror each other (i.e. nonatomic and nonatomic, retain and retain, copy and copy, etc.). From there you just use @synthesize and Objective-C takes care of the rest.
Other @property Modifiers
While speaking of @property modifiers, I should take a moment to discuss them for anyone unfamiliar. You may skip to the next section if you already know the modifiers. By default an @property is atomic, assign (or weak), and readwrite. I've never found a good reason for atomic. Atomic just means that Objective-C is going to use a lock to enforce thread safety. This will make your getters and setters slower since it has to acquire and release the lock. If you are using single-threaded code this is completely unnecessary. If you are using multi-threaded code, you really should be using Grand Central Dispatch (or NSOperationQueue) to take advantage of lockless exclusion.
The next modifier is copy/retain/assign. For almost everything you do, you will use retain. It's good to know the exceptions though. When you have an NSString, NSArray, NSSet, or other object with mutable/immutable variants, you should use copy instead. If the object is immutable, it will be retained for efficiency, otherwise it will be copied.
Assign (or weak when using ARC) also has some uses. Mostly you will use it when dealing with primitives, such as BOOL or int. Since they aren't objects you can't call -copy or -retain on them. You will also use assign when you have want to avoid a retain cycle by creating a weak relationship. This is common with delegates.
You can also specify the names of your getters and setters. You specify getter=myGetterName or setter=mySetterName. This is often used with BOOLs. For example, you may create a property like this:
@property (nonatomic, assign, getter=isOn) BOOL on;
When writing your -dealloc methods, you may wonder how you can release your instance data without direct access to your ivars. There is only one reasonable way to do this. You should call [self setMyProperty:nil];. This will set your ivar to nil and consequently release the old value.
All rules have their exceptions including @property. If you need to make a custom getter or setter, you will need to create an associated ivar. Technically you can actually access synthesized ivars directly. However, Apple considers this to be a “bug” and access to synthesized ivars may disappear in the future. You can add ivars to class extensions for your private properties. Be aware, though, this is only supported on Clang/modern runtime and may require additional settings.
Now that we've gone over all that, let's go over the benefits of these extra techniques:
1. The obvious benefit is the compiler will write your getters and setters for you. The compiler not only writes your getters and setters but also makes them Key-Value Observing (KVO) compliant. This means you can observe the changes of any property on an object. KVO is especially useful when writing Model View Controller (MVC) code. Controllers can watch model objects for any changes and update the views as appropriate.
2. You will have less memory bugs. When writing your actual code, you should always use your getters and setters instead of accessing ivars directly. This will make most of your memory management bugs disappear instantly. You won't need to remember to call retain/copy and release with your ivars. Your synthesized getters and setters will do that automatically for you.
3. Your code will be better encapsulated. I've seen too much code where I've had to guess which ivars (and methods) I could safely use due to a lack of organization and no documentation.
4. Last, but not least, your code will be more readable. Your headers won't be cluttered with private and protected data, and your implementation files won't be cluttered with standard getters and setters. Furthermore, your files will be better organized. You will know exactly where to find your public, private, and protected data.
Employ these techniques and they will serve you well. Other people who have to use your code after you with thank you too.
The ideas in this article were strongly influenced by the Apple docs, iOS Recipes Recipe 35, various WWDC videos, and a lot of personal experience (i.e. bugs).
Update: August 23, 2011
I have further refined and corrected this article through further study and experience on the topic, particularly ivars and custom accessors.