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