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, which is a shortcut to a method. What does that 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 is a property? It is a high-level language feature—a shortcut, if you will—that allows developers to easily access getters and setter methods on instances of a class without having to refer to the getter and/or the setter method at all. If you want to set a property’s value, you simply use the equals sign to do so. If you want to read from the property, you simply point to it using dot notation.

Let’s look at this in detail. In Recipe 1.12, we saw how we create classes. We created a class called Person. Then in Recipe 1.13, we learned how to add methods to our classes. Now, by combining the concepts explored in these two recipes, we can learn more about properties. To start, 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

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 (as well as the overhead necessary for handling such variables), atomic properties are not by default provided in iOS by the runtime. Only apps developed for the Mac can take advantage of both atomic and nonatomic properties. If you want your properties to be atomic, you will need to handle threading and access on your own using locks or other mechanisms that are outside the scope of this book.

In terms of the setter and the getter methods, fortunately, we don’t have to write these two methods for properties manually. The LLVM compiler automatically generates these setter and getter methods by putting a hidden @synthesize in the implementation of our object. This keyword simply creates a getter and a setter method for that property. For instance, if you simply create a property named firstName, the compiler will take the following actions on your behalf:

  • Creates an instance method for you named _firstName. This rule applies similarly to any other property name.

  • Triggers the following synthesize for you in your implementation file without you having to touch it:

    @synthesize firstName = _firstName
  • Under Automatic Reference Counting, it also takes care of deallocating your properties for you.

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 (i.e., 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 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

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

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

  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]);
NSLog(@"Last name = %@", newPerson.lastName);

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

First name = Andrew Jr
First name = Andrew Jr
Last name = Carnegie

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 6 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.