Objective-C Guide For Developers, Part 3

After seeing in the second part of this guide how to create and use objects, now we will see how we can create our own classes.

Table of contents

Classes
Headers, importing and forward declarations
Properties and instance variables
Initialization

Classes

The declaration and the implementation of a class are separated in Objective-C. To declare a new class we first declare the interface using the @interface directive:

@interface ClassName : SuperClass

@end

By convention, class (and type) names start with a capital letter (and don’t forget prefixes as we discussed before. I will omit them in the examples here for clarity, but I advise you to follow the conventions when writing your code).

After the colon the superclass from which our class inherits must be specified. The root class in Objective-C is NSObject. When one object encounters another object, it expects to be able to interact using at least the basic behavior defined by the NSObject class. NSObject offers a lot of basic behaviors like the +alloc and -init methods we have seen in the paragraph about creating objects.

The implementation of the class goes, intuitively enough, in the @implementation part:

@implementation ClassName

@end

Methods are declared in the @interface of the class:

@interface MyClass : NSObject

- (void)doSomething;

@end

and get implemented in the @implementation part. The implementation of the method goes inside curly braces:

@implementation MyClass

- (void)doSomething {
    ...
}

@end

When an object needs to send a message to itself, you can do so from within a method by using the self keyword.

- (void)someMethod {
    ...
    [self someOtherMethod];
    ...
}

To instead access the implementation of a method in the superclass (when you, for example, override one of its methods) you use the super keyword:

- (void)someMethod {
    ...
    [super someMethod];
    ...
}

Headers, importing and forward declarations

In Objective-C the interface and the implementation of a class are usually kept in two different files, unlike other languages where everything is kept in a single file. The interface goes in a header file with .h extension, while the implementation goes in a file with .m extension.

It is possible to declare interface and implementation of classes in only the .m file, which can contain more than one class. This is used if you want to declare an internal class that is used only in a single class implementation and not anywhere else. While some other languages have special constructs for this, in Objective-C you accomplish it by just putting the new class in the implementation file.

To reference class header files, Objective-C uses the #import directive. Always use this instead of the #include coming from C, also if you are importing C files, because #import will check automatically for double inclusion of headers so you will never have problems with recursive inclusion. #import uses angular brackets < > for global inclusions and double quotation marks ” “ for local ones. So if you are including a framework you do it like this:

#import <Foundation/Foundation.h>

while for your own classes in the project:

#import "MyClass.h"

Sometimes you need to have circular dependencies in some classes, where a class A needs a class B, and B needs A.

@interface ClassA : NSObject

- (ClassB *)methodThatReturnsAnInstanceOfClassB;

@end

@interface ClassB : NSObject

- (ClassA *)methodThatReturnsAnInstanceOfClassA;

@end

This code cannot compile, because ClassA needs to know about ClassB, which at that point still has not been declared. This happens also with Objective-C protocols, as we will see later in this guide. You can use a forward declaration to avoid this problem, with the @class keyword:

@class ClassB;

@interface ClassA : NSObject

- (ClassB *)methodThatReturnsAnInstanceOfClassB;

@end

@interface ClassB : NSObject

- (ClassA *)methodThatReturnsAnInstanceOfClassA;

@end

Forward declarations are often used in header files also to avoid importing the header of another class, which would import all the declarations present in the latter (classes, protocols, categories, types and constants) which, in turn, would also spread in all the files that import the header that includes it.

Properties and instance variables

We now need to know how to store the internal state of an object. It is a best practice in Objective-C to use properties for this purpose, which are declared in the interface of the class.

@property Type propertyName

Let’s use the typical example for an class: the Person class. If we want to declare a class to stores the first and last name of people, as well as their age, this would be its declaration:

@interface Person : NSObject

@property NSString *firstName;
@property NSString *lastName;
@property NSUInteger age;

@end

We can now use the dot syntax present in many languages to read or assign values to properties:

Person *person = [Person new];
person.name = @"Matteo";

When you declare a property, there are many things happening behind the scene. In the first place, a corresponding instance variable is automatically synthesized by the compiler for each property you declare to store the value of the property. Although it’s best practice to access the properties through the dot syntax, you might want to access these instance variables directly (for example, as we will see, to change a readonly property, in initializers, deallocation or custom accessors, or to avoid triggering key-value coding notifications). The name of the synthesized instance variable is the name of the corresponding property prefixed with an underscore.

(void)someMethod {
    _firstName = @"Some other name";
}

You can change the name of the instance variable sinthesized for a property if you want, declaring it explicitly with the @synthesize keyword in the class @implementation section:

@implementation ClassName

@synthesize propertyName = differentInstanceVariableName;

...
@end

For example, we can rename the instance variables in our Person class

@implementation Person

@synthesize firstName = ivar_firstName;
...
@end

You can also declare your own instance variables without a relative property. You can do so either in the interface of a class, to make these variables visible to subclasses:

@interface ClassName : SuperClass {
    Type _myInstanceVariable;
}
...
@end

or in the implementation of the class:

@implementation ClassName {
    Type *_myInstanceVariable;
}
...
@end

Accessors methods

Another thing that happens behind the curtains when declaring properties is that the compiler synthesizes automatically corresponding accessors methods. Objective-C is not like other languages where properties access directly the memory where the value for a property is stored. What happens instead is that every time you use a property with the dot syntax, that is translated into the corresponding method and a message is sent to the object, in the exact same way as if you were calling a method.

The method used to access the value, called the getter method, has the same name as the property. The method used to set the value, called the setter method, instead starts with the word “set” and then uses the capitalized property name.

So, in our Person class, when we declare the firstName property, these two methods are added to the class:

- (NSString *)firstName;
- (void)setFirstName:(NSString *)firstName;

You can use these methods yourself to read and write the value of the property, which is what happens anyway when you use the dot notation.

Because of this automatic translation I have seen some developers mistake some method call for a property, and also XCode (the IDE used to develop for iOS and Mac OS X) will autocomplete it anyway. I’ve seen this a lot, for example, with the count method of arrays.

NSUInteger numberOfItemsInTheArray = someArray.count;

The NSArray class has no property called count, but only a method. But this line of code will work anyway because of how properties work in Objective-C. The correct syntax would be:

NSUInteger numberOfItemsInTheArray = [someArray count];

Which is more consistent because the former implies the existence of a property that does not exists.

The accessor methods are synthesized automatically by the compiler, but you can provide your own ones if you want. This is one of the cases I mentioned where you actually need to access the instance variables behind properties directly.

For getter methods a common implementation is:

- (Type)property {
    return _property;
}

And for setter methods:

(void)setProperty:(Type)property {
    _property = property;
}

These are basic implementation that only read and write values of a property, but you might want to start from here to add further behavior to the accessors.

Pay attention that if you implement both the accessor methods for a property yourself, the compiler will assume that you are taking control of the property and won’t synthesize the instance variable for you. If you still need it, you need to declare it yourself in the class implementation:

@synthesize property = _property;

Sometimes you might want to declare a property as readonly. This happens when you create a property that is derived from other values and cannot be set, or simply when you want to have immutable objects or properties not changeable from an external object. If, for example we want to add a fullName property to our Person class, composed by the first name followed by the last name, we will declare this property as readonly:

@property (readonly) NSString *fullName;

We then need to provide a custom accessor method for it:

- (NSString *)fullName {
    return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}

For readonly properties, providing the getter method is enough to prevent the compiler from synthesizing a corresponding instance variable.

The opposite of readonly is readwrite. Usually there’s no need to specify the readwrite attribute explicitly, because it is the default, but there is one case in which you might want to do it anyway. Sometimes you might want to declare a property as readonly for external objects only, but still be able to change its value from inside the object itself. You can do so by accessing the instance variable directly, but you might prefer to still use the property internally, for example to trigger Key-Value Coding notifications (which we will see later). In that case you can declare the property as readonly in the interface of the class and then redeclare it as readwrite in the interface extension, which we will see later.

Initialization

As we have seen previously, while other languages have constructs to create and initialize object, in Objective-C an object is created with the +alloc method and initialized though an initializer, often the -init method or another initializer that takes some parameters needed at the object creation moment.

All initializers in Objective-C start with the init word and have a return type id. As a rule an initializer should always be called in a nested call with the +alloc method:

MyClass *object = [[MyClass alloc] init];

This is done because the initializer might not return the same object that was created with the +alloc method. There are different reasons for this to happen: a class might be a singleton, thus allowing only one instance of it to exist at any time. In this case an init method will return that instance, if it already exists, discarding the one coming from +alloc.

If an initializer takes some unique identifier as a parameter, the init method might retrieve that object if it exists and return it, again discarding the one created with +alloc. This is not a singleton, since many instances of the class are allowed, but still specific instances are unique.

When you are writing your own classes it’s very likely that you need to implement your own initializers to setup your objects properly. If you don’t need any parameter when initializing an instance, you simply override the -init method inherited from NSObject:

- (id)init {
    if (!self = [super init])
        return nil;

    ... // Instance variables are set here
    return self;
}

The first thing you need to do when implementing an initializer, is to call the initializer of the superclass first. If the result is nil, it means that something went wrong with the initialization and you have to return nil yourself.

You might have notice that in the condition of the if the result of the initializer of the superclass is assigned to self. This is again for the same reason: that method might return a different instance, substituting the current one.

The assignment in the if condition is actually a syntax that is allowed by C. In C the assignment operator also returns the value that gets assigned, so it can be tested in an condition. I personally consider it a bad programming practice, because it does two things at the same time, hiding one of them (the assignment) and making it a side effect. This is the source of many programming errors, where some developer that wants to check for equality uses the = operator by mistake instead of the == operator. If you actually make yours the practice of testing assignments directly this kind of errors will be even harder to spot, since they will become invisible to your eyes.

This said, it is idiomatic to do this in an initializer in Objective-C, so I consider this case, and only this case, acceptable. My advice is to avoid it in all other cases.

After the call to super, you can and usually initialize the instance variables to their initial value and return self at the end of the method. You should always access the instance variables directly from within an initialization method instead of using properties. This is because at the time a property is set, the rest of the object may not yet be completely initialized, creating undefined behavior. Another reason is that properties might trigger Key-Value Coding notifications and cause side effects. Even if you don’t provide custom accessor methods or know of any side effects from within your own class, a future subclass may override the behavior and create them.

Keep in mind that this form of initializer where you return nil on failure of the initializer of the superclass or return self at the end is the most common type but not the only one. As I said before, you might perform additional checks to see wether the initialization can proceed and return nil at any point for other reasons, or retrieve some other instance of the class and return that one instead of self.

Designated initializers

Sometimes a class provides multiple initializers that take data in different forms. In this case one of the initializers should be chosen as the designated initializer for the class. This initializer has to ensure that all the inherited instance variables are initialized by invoking the designated initializer of the superclass. The designated initializer is typically the one with the most parameters and which does most of the initialization work. The secondary initializers should call this initializer instead of calling the designated initializer of the superclass themselves.

To take our Person class as an example again, we can implement an initializer which takes the first name, the last name and the age as parameters, to make sure that we create objects that are immediately populated correctly:

- (id)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName age:(NSUInteger)age {
    if (!self = [super init])
        return nil;

    _firstName = firstName;
    _lastName = lastName;
    _age = age;
    return self;
}

Le’ts suppose that we then want a convenience initializer that takes the full name as a single string where first name and last name are separated by a space, since it might come in this form from some source, like a web service or database. The designated initializer for the Person class would then be the one we already implemented and this new convenience initializer would call it:

- (id)initWithFullName:(NSString *)fullName age:(NSUInteger)age {
    NSArray *fullNameComponents = [fullName componentsSeparatedByString:@" "];
    return [self initWithFirstName:fullNameComponents[0] 
                          lastName:fullNameComponents[1]
                               age:age];
}

Please do not take this as a real world example since it lacks all the input validation and makes too many assumptions about people names.