How to Use Declared Properties in Your iOS App

By Jesse Feiler

Although properties and instance variable access and accessors are often mashed together in the minds of iOS app programmers, make sure that you understand the unique nature of properties and how they really work.

Whereas methods are concerned with sending messages to objects to get things done, properties are concerned with the state of the objects. Frameworks and other objects behave based on what they find in their properties (hence you can modify object behavior by changing a property).

You also may want to know something about the state of the object, such as its color, or about a window’s Root view controller.

Your app’s view controllers, which act as a bridge between the views and the model, need to be able to find the model object to get data and send it updates. All of this is done using properties. A property looks like the following:

@property (strong, nonatomic) IBOutlet UIImageView *car;

But not all properties are outlets. If you select the RTAppDelegate.h file in the Project inspector, you can see that it includes a property:

@property (strong, nonatomic) UIWindow *window;

As you can see, the order of the attributes (strong, nonatomic versus nonatomic, strong) doesn’t matter.

What comprises a declared property

A declared property has two parts: its declaration and its implementation.

The declaration uses the @property keyword, followed by an optional parenthesized set of attributes, the type information, and the name of the property.

Access to properties is implemented by accessor methods (although within the class that declares the property, the property can be accessed directly, just as instance variables are). You can write your own accessor methods or you can let the compiler do it for you.

The default names for the getter and setter methods associated with a property are whateverThePropertyNameIs for the getter and setWhateverThePropertyNameIs: for the setter. In the case of trip, the getter method is trip, and the setter method is setTrip:.

To access the trip property in the appDelegate, you would use

AppDelegate* appDelegate =
    [[UIApplication sharedApplication] delegate];
Trip* thisTrip = [appDelegate trip];

or to set that property, use

AppDelegate* appDelegate =
    [[UIApplication sharedApplication] delegate];
[appDelegate setTrip:newTrip];

delegate, by the way is a UIApplication property.

Using dot syntax

Objective-C provides a dot (.) operator that offers an alternative to square bracket notation ([]) to invoke accessor methods. You use dot syntax in the same way you would when accessing a C structure element:

Trip* thisTrip = appDelegate.trip;

or to set that property, use

appDelegate.trip = newTrip;

When used with objects, however, dot syntax acts as “syntactic sugar” — it’s transformed by the compiler into an accessor message. Dot syntax doesn’t directly get or set an instance variable. The code examples using it are the exact equivalent to using the bracket notation.

Many programmers like the dot syntax because it may be more readable; just think of those bracket notation situations where you’re accessing a property that is a property of another object (that itself is a property of another object, and so on).

The real advantage of dot syntax, though, is that the compiler will generate an error when it detects an attempt to write to a read-only declared property. This is so much better than having to settle for an undeclared method warning because you invoked a nonexistent setter method, with the app subsequently failing at runtime.

When you use the compiler to create accessor methods for you, the compiler creates an instance variable of the type you have declared that it will then use to store and retrieve the property value with the name of the property. For example for the following property:

@property (weak, nonatomic) IBOutlet UIImageView *car;

the statement

@synthesize car;

generates an instance variable with the name of car of UIImage type .

However, if you let the compiler automatically generate an @synthesize statement for you, it actually uses an instance variable name beginning with an underscore character, so you would get the following code generated for you behind the scenes:

@synthesize car = _car;

This allows you to distinguish between the property name (accessed by self.car) and the instance variable name (accessed simply as _car).

Apple recommends that you use the property reference (self.car) in normal methods, but use the _car variable in init methods. This applies only to the code within a file. Code elsewhere in your app accesses the property as it is declared in the @interface section of the .h.

There is no way that code anywhere in the app except in the .m file of a class can access the instance variable directly when you let the compiler do the work. And that’s a good thing to ensure that encapsulation is properly provided.

Writing your own accessors

You don’t have to use the accessors generated by the compiler; and, sometimes, it even makes sense to implement them yourself. If you implement the accessor methods yourself, you should make sure that your approach matches the attributes you’ve declared.

For example, if you have a lot of overhead to create an object that might not be used, you can create your own getter accessor that creates the object the first time it’s accessed. In addition, writing your own accessor means you don’t have to have an instance variable associated with the property.

You could, for example, have an area property on a object representing a rectangle. The getter for area might perform length x width and never bother with an instance variable.

Accessing instance variables with accessors

If you don’t use self, you access the instance variable directly. In the following example, the set accessor method for _currentDestinationIndex isn’t invoked:

_currentDestinationIndex = [[NSUserDefaults
 standardUserDefaults]objectForKey:CurrentDestinationKey];

The preceding isn’t the same as

self.currentDestinationIndex = [[NSUserDefaults
 standardUserDefaults]objectForKey:CurrentDestinationKey];

To use an accessor, you must use self.