11.2. Quick Tour

11.2.1. Objects and Classes

Let's start with a simple class demonstrating the syntax added to the C language. Like C++, Objective C separates the interface definition of a class from the implementation. Here is the interface for our familiar Person class. Ideally, this interface is put into a separate file, e.g., Person.h, so that other classes can include the declaration just like any other C header file. But Objective C does not enforce any file policy and we can keep multiple interfaces and implementations in a single file.

#include <Object.h>

@interface Person : Object {
  const char* name;
  int age;
}
- init;
- (void)display;
- (const char*)name;
- name: (const char*)aName;
@end

The interface definition starts with @interface and ends with @end. All Objective C directives use keywords starting with an "at" sign @. Next we see the name of the class and the name of the base class separated by a colon. Here, we use the base class Object which is part of GNU's Objective C environment. The class names are followed by a block of variables which looks like the body of a C structure. In fact, these fields are the instance variables of the class. The remaining lines contain the decisive new features. The minus sign indicates an instance method (as opposed to a class method which starts with a plus sign). The method declarations use a syntax which combines Smalltalk's message declaration with C types. The first method is has no argument and returns the object itself (the default like in Smalltalk). The display method does not return anything. The next two methods are the accessor methods for the attribute name. The getter (const char*)name has no argument and returns a C string. The setter takes a C string aName as an argument and like init returns the object itself. As usual for C, the declarations are determined with a semicolon. Next, let's look at the implementation of the class.

@implementation Person
- init {
  name = "Homer";
  age = 55;
}
- (void)display {
  printf("name=%s, age=%d\n", name, age);
}
- (const char*)name { return name; }
- name: (const char*)aName { name = aName; }
@end

In analogy to the interface, the implementation is enclosed in a pair of @implementation and @end directives. All the information defined in the interface does not have to be repeated anymore (but it can, e.g., to define initial values for the instance variables). The main part of the implementation defines the methods. Again, the syntax combines Smalltalk with C. For each method, the signature copied from the interface is followed by a C block of statements. The statements can access the instance variables of the class directly.

Now that we have defined our Person class, let's try and use it.

main() {
  Person* person = [[Person alloc] init];
  [person display];
  printf("name=%s\n", [person name]);
  [person name: "Frank"];
  printf("name=%s\n", [person name]);
  [person free];
}

name=Homer, age=55
name=Homer
name=Frank

With our Smalltalk background the new syntax is not hard to follow. [1] Objective C allows us to use Smalltalk's message passing enclosed in square brackets anywhere in the C code. The first statement sends the built-in alloc message to the Person class object which return a new empty instance of Person. We then pass the init message to this new object which causes our initialization code to be executed. The result is the initialized Person instance which can be assigned to a Person pointer. The next line sends the display message to this new instance printing name and age of the person. Next, we call the getter for the attribute name as part of the printf statement. As an example of argument passing, the new name Frank is passed with the message selector name to the person object. We then print the name again to see if it has really been changed. Finally, the memory allocated for the person object is returned to the operating system using the free message defined in the Object class.

11.2.2. Message Passing and Inheritance

The similarities to Smalltalk are not only syntactical. The semantics are very similar as well. When a message is passed to an object, the system decides at run-time which method to call. It does not need to be known at compile-time which messages an object understands. As we will see, this is in sharp contrast to the other object oriented members of the C family which rely on compile-time method dispatching.

The messaging becomes clearer if we replace the pointer type Person* by the generic object identifier id.

main() {
  id person = [[Person alloc] init];
  [person display];
  printf("name=%s\n", [person name]);
  [person name: "Frank"];
  printf("name=%s\n", [person name]);
  [id free];
}

Now, the program does not know the type of person at compile-time anymore, but the program behaves exactly the same. The main difference is that the first version using the explicit type Person* will be checked at compile-time. If we try to call a message a Person does not respond to, we will get a compile-time error. Using the generic id, the problems shows only at run-time.

Just like Smalltalk, a method can send a message to itself or its superclass using self and super, respectively. The latter is particularly useful for the initialization methods.

@interface Employee : Person {
  int number;
}
- (int)number;
}
@end

@implementation Employee
- init {
  [super init];
  number = 100;
}
- (void)display {
  [super display];
  printf(", number=%d", [self number]);
}
- (int)number { return number; }
@end

main() {
  id employee = [[Employee alloc] init];
  [employee display];
  [employee free];
}

name=Homer, age=55, number=100

11.2.3. Categories and Protocols

In Smalltalk we were able to extend an existing class by simply adding a new method to it. In Objective C, an extension of an existing class is defined using a new interface and implementation with the same class name and marking it with a so-called category. The category name follows the class name in parentheses. The next example adds a getter for the age attribute.

@interface Person (Accessor)
- (int)age;
@end

@implementation Person (Accessor)
- (int)age { return age; }
@end

main() {
  id employee = [[Employee alloc] init];
  printf("age=%d", [employee age]);
}

This category adds the getter method to the Person class and all its subclasses. As in Smalltalk, this feature avoids the utility classes found in less dynamic object oriented languages. Note that the new methods have access to the attributes just like the original methods, but is not possible to add new attributes to a class.

Objective C's interfaces correspond to classes in the other object oriented languages of the C family. But there is also the option to define a pure interface, that is, a collection of method signatures which other classes can implement. In Objective C this is called a protocol. As an example, we encapsulate the ability to display oneself as a Displayable protocol.

@protocol
- (void)display;

}

A class can list the protocols it adopts in angle brackets after the superclass.

@interface Person : Object <Displayable>
...
@end

main() {
  id person = [[Person alloc] init];
  printf("displayable=%d", [person conformsTo: @protocol(Displayable)]);
  [person free];
}

1

At run-time we can check if a given object conforms a protocol. The @protocol directive converts the protocol name to a protocol object which is passed with the conformsTo message. Protocols are not as needed in a language such as Objective C or Smalltalk, since any message can be sent to any object. However, protocols allow the Objective C compiler to do some additional type checking and thus avoid run-time errors. To do so, the protocols an object is supposed to conform to can be explicitly mentioned as part of the identifier type.

main() {
  id <Displayable> person = [[Person alloc] init];
  [person display];
  [person free];
}

In this case, the compiler makes sure that the class assigned to the person variable complies with the protocol (or protocols) listed in angle brackets behing the id type. It also checks if the messages passed to person are part of the protocol. Trying to pass the another message (such as age) will cause a compile-time error although the underlying Person class is able to respond to the message. The use of protocols thus allows for the same compile-time type checking as the use of interfaces in Java or C#.

It is not unusal in object oriented systems that an existing class has to be adapted to a new protocol defined somewhere else, for example, when interfacing with a third party library. If the original class can't be changed, the only solution is an adapter class which delegates the new methods to the old class. In Objective C, a protocol can be added to an existing class by defining it in a category.

@protocol Printable
- (void)print: (FILE*)stream;
@end

@interface Person (Print) <Printable>
@end

@implementation Person (Print)
- (void)print: (FILE*)stream {
  fprintf(stream, "name=%s, age=%d", name, age);
}
@end

main() {
  id <Printable> person = [[Person alloc] init];
  [person print: stdout];
  [(Object*)person free];
}

In this example, we define a new protocol Printable which allows for printing an object on a stream. The Person is adopted to this protocol by adding the required method with the category Print. Note that we have to cast the id to an Object pointer in order to sent it the free message, since this message is not part of the Printable protocol.

Notes

[1]

But the deviation from the typical C function call syntax is probably one of the reasons that Objective C did not get as popular as C++.