1.16. Adding Properties to Classes

Problem

You want to add properties to your classes so that you can take advantage of dot notation to access those values, as opposed to using methods on your classes.

Solution

Define properties in your classes using the @property keyword.

Discussion

Anything addressed via dot notation is a property. Properties are shortcuts to methods. What do I mean? Well, let’s have a look at an example:

NSObject *myObject = [[NSObject alloc] init];
myObject.accessibilityHint = @"Some string";

You can see that we allocated and initialized an object of type NSObject and used dot notation to access a property called accessibilityHint in that object. Where did accessibilityHint come from?

It’s quite simple. A property is defined using the @property keyword. In fact, if you hold down the Command key on your keyboard in Xcode, and simply click on the accessibilityHint property in the example that we just saw, you will be redirected to the NSObject.h file where you will see this:

@property(nonatomic, copy) NSString *accessibilityHint;

But what are properties? Well, when we define a property, we tell the compiler that we will write a setter and a getter method for this property. If somebody tries to set a value in this property, the runtime will execute our setter method. If somebody tries to read from the property, the runtime will execute the getter method.

Let’s see this in detail. In Recipe 1.12 we saw how we create classes. We created a class called Person. In Recipe 1.13 we learned how to add methods to our classes, so let’s combine the two to learn more about properties. Let’s go to the Person.h file and define a property called firstName:

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic, strong) NSString *firstName;

@end

Now try to compile your program by pressing Command+Shift+R. Note that the LLVM Compiler will give you two warnings:

warning: property 'firstName' requires method 'firstName' to be defined - 
      use @synthesize, @dynamic or provide a method implementation [3]
warning: property 'firstName' requires method 'setFirstName:' to be defined - 
use @synthesize, @dynamic or provide a method implementation [3]

Note

You will learn all about new Automatic Reference Counting keywords, such as strong, in Recipe 1.17.

A nonatomic property is a property that is not meant to be accessed and changed by multiple threads at the same time. Such a property or variable is not thread-safe. A thread-safe (atomic) variable will prevent multiple threads from writing to it at the same time, or a thread from reading it while another thread is writing to it. For performance reasons and the overhead that is involved in order to handle such variables, atomic variables are not present by default in iOS.

It’s obvious that the property has been created, but the compiler doesn’t know what to do in case somebody tries to read this property or assign a value it. For this reason, we have to write a setter and a getter method. The compiler is clearly telling us that the setter method should be called setFirstName: and the getter method should be called firstName. Fortunately, we don’t have to write these two methods for properties manually. We can use the @synthesize keyword in the .m file to let the compiler generate the setter and the getter methods for our properties automatically:

#import "Person.h"

@implementation Person
@synthesize firstName;

- (id)init
{
  self = [super init];
  if (self) {
    // Initialization code here.
  }

  return self;
}

@end

Now we can go ahead and use our Person class. Here is an example:

#import "SomeOtherClass.h"
#import "Person.h"

@implementation SomeOtherClass

- (void) makeNewPerson{

  Person *newPerson = [[Person alloc] init];
  newPerson.firstName = @"Andrew";
  NSLog(@"First name = %@", newPerson.firstName);
  NSLog(@"First name = %@", [newPerson firstName]);

}

@end

The example code prints the first name of newPerson twice, first using its firstName property and then by calling the firstName getter method on that object. Both will point to the same method, which @synthesize created for us in the Person.m file.

Note

In an older version of the Objective-C runtime, for @property to work, we also had to define an instance variable. An instance variable is a variable whose memory management is done by the programmer herself. Instance variables are also not exposed to classes outside the scope of the class that defines them (that is, they are not exposed to any class that simply imports the class with the instance variable). Instance variables are normally called ivars by professional Objective-C developers (ivar is pronounced I-WAR, with the V pronounced as a W).

With the new runtime, we don’t have to define ivars anymore. We simply define the property and the LLVM compiler defines the ivar for us. If you are using the GCC compiler, which is rather unlikely, you will see big differences from how the LLVM compiler treats ivars. For instance, in GCC 4.2, an ivar is not accessible to any subclass of a class, whereas if you are using LLVM Compiler, a subclass of a class can use its superclass’s ivars. So make sure you are using Apple’s latest compiler, which is LLVM. If a property is read-only, the only way that property’s value can change is for the class that defines that property to use the ivar of that property to change the property’s value.

If you want to fiddle around with setter and getter methods, you are free to do so. Even if you have used @synthesize to allow the compiler to generate the setter and getter methods of a property for you, you can still go ahead and override those methods. For instance, in this example, I change the setFirstName: setter method of the firstName property of the Person:

#import "Person.h"

@implementation Person
@synthesize firstName;

- (void) setFirstName:(NSString *)paramFirstName{
  firstName = [paramFirstName stringByAppendingString:@" Jr"];
}

- (id)init
{
  self = [super init];
  if (self) {
    // Initialization code here.
  }

  return self;
}

@end

I have overridden the setter method of my firstName property to add a “ Jr” suffix to any string that I am told to assign to the firstName property. So when the setter and getters are invoked, as before:

Person *newPerson = [[Person alloc] init];
newPerson.firstName = @"Andrew";
NSLog(@"First name = %@", newPerson.firstName);
NSLog(@"First name = %@", [newPerson firstName]);

We will get the following printed out to the console window:

First name = Andrew Jr
First name = Andrew Jr

If you want to define a read-only property, all you have to do is to define your property using the @readonly keyword, like so:

@property (nonatomic, strong, readonly) NSString *lastName;

Get iOS 5 Programming Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.