Home / iPhone Tips and Tutorials

Understanding Declared Properties

Although properties and instance variable access and accessors are often mushed together by programmers, make sure that you understand properties and how they really work.

Whereas methods are concerned with sending message to objects to get things done, properties are concerned with the state of the object. Framework and other objects behave based on what they find in their properties; for example, a button's background image is a property you set (indirectly, in Interface Builder).

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.

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 window property:

@property (strong, nonatomic) UIWindow *window;

You add a trip property to RTAppDelegate:

@property (nonatomic, strong) Trip *trip;

The order of the attributes doesn't matter.

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, they 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. To have the compiler do it for you, you use the @synthesize directive to tell the compiler to create the accessor methods. The code it generates matches the attributes you have specified.

The default names for the getter and setter methods associated with a property are whateverThePropertyNameIs for the getter and setWhateverThe PropertyNameIs: 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

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

or to set that property

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

The UIApplication is a singleton object (there is just one). To get a reference to it, you send the class message (in Objective-C, you can send messages to classes, which are really objects on their own). Sending the UIApplication object the delegate message gives you a pointer to the delegate object.

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 as you would to access a C structure element:

Trip* thisTrip = appDelegate.trip;

or to set that property

appDelegate.trip = newTrip;

When used with objects, however, dot syntax acts as "syntactic sugar" - it is transformed by the compiler into an accessor message. Dot syntax does not 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 than when you are accessing a property that is a property of another object (that 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 instead of 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 will create an instance variable of the type you have declared that it will use to store and retrieve the property value with the name of the property. For example, in the following:

@synthesize car;

an instance variable with the name of car and the type of UIImage is generated. You can, however, specify the instance variable name. For example, in RTAppDelegate.m in Xcode, you will find

@synthesize window = _window;

You'll see this quite a bit, and specify the instance variable name this way as well. By prefixing an underscore, you can distinguish between a local variable and direct access to a property that's not using an accessor method.

Setting attributes for a declared property

You can set certain property attributes when you declare a property. I cover some of those attributes in this section.

Setter semantics/ownership

  • strong (similar to retain, which was used previous to ARC) creates an accessor method that means that the object this property points to will be retained while it is in scope (or not set to nil or some other value).
  • weak (similar to assign, which was used previous to ARC) creates an accessor that uses simple assignment. This attribute is the default, and you typically use this attribute for scalar types such as NSInteger and CGRect, or (in a reference-counted environment) for objects you don't own, such as delegates and to avoid retain cycle problems, as explain in "Understanding the deadly retain cycle," earlier in this article.
  • copy specifies that a copy of the object should be used for assignment. The previous value is sent a release message.
    The copy is made by invoking the copy method. This attribute is valid only for object types, which must implement the NSCopying protocol.

For object properties, you must explicitly specify one of these; otherwise, you get a compiler warning. So you need to think about what memory management behavior you want, and type the behavior explicitly.

Writability

These attributes specify whether a property has an associated set accessor. They are mutually exclusive.

  • readwrite indicates that the property should be treated as read/write. This attribute is the default. If you use the @synthesize directive, the getter and setter methods are synthesized; otherwise, both a getter and setter method are required.
  • readonly indicates that the property is read-only. If you use the @synthesize directive, only a getter method is synthesized. Otherwise, only a getter method is required. If you attempt to assign a value using the dot syntax, you get a compiler error.

Accessor method names

The default names for the getter and setter methods associated with a property are propertyName and setPropertyName:, respectively. For example, for the property trip, the accessors are trip and setTrip:. You can, however, specify custom names instead. They are both optional and can appear with any other attribute (except for readonly in the case of setter =):

  • getter = getterName specifies the name of the get accessor for the property. The getter must return a type matching the property's type and take no parameters.
  • setter = setterName specifies the name of the set accessor for the property. The setter method must take a single parameter of a type matching the property's type and must return void.

Typically, you should specify accessor method names that are key-value coding compliant. A common reason for using the getter decorator is to adhere to the isPropertyName convention for Boolean values.

Atomicity

You can use this attribute to specify that accessor methods are not atomic. (There is no keyword to denote atomic.) If you specify nonatomic, a synthesized accessor for an object property simply returns the value directly. Otherwise, a synthesized get accessor for an object property uses a lock and retains and autoreleases the returned value.

Writing your own accessors

You don't have to use the assessors generated by the compiler, and sometimes it makes sense to implement them yourself. If you implement the accessor methods yourself, you should ensure that your approach matches the attributes you have declared. (For example, if you specify copy, you must make sure that you do copy the input value in the setter method).

For example, if there is 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 tine it is accessed. In addition, writing your own accessor means you don't have to have an instance variable associated with the property. The UIView frame property, for example, is actually computed based on the bounds and center properties.

Accessing instance variables with accessors

If you do not use self., you access the instance variable directly. In the following example, the set accessor method for currentDestinationIndex is not invoked:

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

The preceding is not the same as

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

To use an accessor, you must use self.

[Previous] [Contents] [Next]