Free Trial

Safari Books Online is a digital library providing on-demand subscription access to thousands of learning resources.


  • Create BookmarkCreate Bookmark
  • Create Note or TagCreate Note or Tag
  • DownloadDownload
  • PrintPrint
Share this Page URL
Help

Hour 3. Simplifying Your Code > Using Shorthands

Using Shorthands

A very common pattern in object-oriented design is the use of accessors (getters and setters) to change object state. The dot notation provides a simpler syntax for using accessors. Objective-C provides a way to automate the process (this is good because writing accessors can be quite boring).

Using the Dot Notation

Dot notation provides a simple notation to invoke accessors. Assuming controller is an instance of CalculatorController and CalculatorController has a getter model that returns a CalculatorModel, then the following lines are equivalent and compile to the same executable:

double v = [[controller model] value];
double v = controller.model.value;

Similarly,

[[controller model] setValue:1.1];
controller.model.value = 1.1;

The notation understands compound assignments:

[[controller model] setValue:(1.0 + [[controller model] value]) ];
controller.model.value += 1.0;

A warning will be emitted if accessors do not use the following type pattern. For instance, setters that return values (such as a BOOL) will cause a warning:

– (Type) value;
– (void) setValue:(Type)v;

Using this notation, we can write CalculatorController’s dealloc as follows:

– (void) dealloc;
{
  self.textView          = nil;
  [model release]; model = nil;  // model has no setter
  [super
							dealloc];
}

Dot notation has the same pitfalls as accessors do. In the following example, you’ll get 0 on the simulator and a random value on the iPhone because str is nil and rangeOfString: returns a structure.

NSString* str = nil;
int      oslo = [str rangeOfString:@"Oslo"].location;

Did you Know?

The implementation of dot notation is extremely simpleminded. If something looks like a getter, it can be used like one. For instance, retain looks like a getter:

CalculatorModel* model = controller.model.retain;

This is not a recommended practice, but you might encounter it when reading other people’s code.


Advantages of Dot Notation

If a property only has a getter, and you try to write to it with dot notation, the compiler will signal an error:

  error: object cannot be set – either readonly property or no setter found

If a property only has a setter, you cannot read it or write it with dot notation:

  error: request for member '...' in something not a structure or union

On the other hand, invoking a getter or a setter directly might or might not generate a warning.

Disadvantages of Dot Notation

There are three disadvantages to dot notation:

  • It makes searching for properties in source-code files harder (search for “.property =” versus setProperty:).

  • It encourages you to chain many properties, which, like nesting method invocations, makes debugging nil issues harder.

  • It does not work with id types.

Although Apple claims dot notation works with the id type, it does not. You can’t write

[array objectAtIndex:0].text = @"foo";

but you can write

[[array objectAtIndex:0] setText:@"foo"];

Property Specifications and Synthesis

Property specification lets you specify how accessors behave. For instance, you can state whether a property will be retained. Synthesis automatically generates accessors based on the specification. Again this saves you from writing boring code.

Property Specification

A problem with simply declaring a setter is that its users cannot know how it behaves. For instance, although most setters retain their argument, some don’t, and some copy it. Programmers must rely on the source code (if available), the documentation (if correct), or reverse engineering to know what happens.

Property specifications are placed in interfaces and are written as follows:

@property
								(attributes) type name;

The optional list of (attributes) is comma separated. Default attributes need not be mentioned. type is the type of the value being accessed, and name is its name. For instance:

@interface
									CalculatorModel : NSObject
{ float     value;
  ...  }

@property
									float value;

...
@end
							

The property specification replaces the accessor declarations. If you implement the accessors yourself, be aware that the property specification is simply a declaration of intent, and is not enforced by the compiler.

The following sections examine the three ways in which accessors can differ.

Writability

By default, all properties are readwrite—that is, they have both a setter and a getter. You can, however, specify that they only have a getter with the attribute readonly. For instance:

@interface
										CalculatorController : NSObject
{ CalculatorModel* model;
  UITextView*      textView;  }

@property (readonly, retain) CalculatorModel* model;
@property (retain)           UITextView*      textView;
...
@end
								

Memory

By default, all properties are assigned. A copy of the argument is made. This makes sense for all nonpointers.

However, pointers are generally qualified either by retain or copy attributes. Usually, objects want to own the objects to which their pointers reference, so that they know their pointers are valid. They can do so either by retaining the object (as we did in setTextView:) or by copying the object. We will encounter a rare counterexample of an assign to a pointer property specification in Hour 11, “Displaying Tables.”

Atomicity

By default, all properties are assumed to be atomic—that is, the accessors are thread safe. Thread safe accessors guarantee that when you use the getter, you obtain a pointer to a valid object. They do not guarantee that other threads will not change the contents of the object you are given.

If you know your objects will not be used by multiple threads, use the nonatomic attribute. Thread-unsafe accessors make no guarantees with respect to threads, but they are faster.

Synthesis

As property specifications completely define the behavior of accessors, Objective-C can generate code for them. To do this, add the following to @implementation:

@synthesize
								name, name2...;

For instance, the three nonatomic memory variants are as follows:

@property   (nonatomic, memory attribute) id pointer;
@synthesize pointer;

memory attributeGenerated Code
ASSIGN:{ pointer = p; };
RETAIN:{ if (p != pointer)
 { [pointer release]; pointer = [p retain]; } }
COPY:{ if (p != pointer)
 { [pointer release]; pointer = [p copy]; } }


Atomic getters and setters use a lock on the object to guarantee thread-safe access. Furthermore, getters retain, then autorelease the result, so that it is still available if other threads release it.

In the next hour, you’ll learn about the different variants of copy. If you need to use a different way of copying, such as mutableCopy, you must implement the getter and setter by hand.