This chapter describes some linguistic and structural features of Objective-C having to do with classes; in the next chapter, we’ll do the same for instances.
In Objective-C, as in many other object-oriented languages, a mechanism is provided for specifying a relationship between two classes: they can be subclass and superclass of one another. For example, we might have a class Quadruped and a class Dog and make Quadruped the superclass of Dog. A class may have many subclasses, but a class can have only one immediate superclass. (I say “immediate” because that superclass might itself have a superclass, and so on in a rising chain, until we get to the ultimate superclass, called the base class, or root class.)
Because a class can have many subclasses but only one superclass, we can imagine all classes in a program as being arranged in a tree that splits into branches, such that each branch splits into smaller branches, each smaller branch splits into even smaller branches, and so on. Or we can imagine all the classes arranged in a hierarchy, such as might be displayed in an outline, with a single ultimate superclass, then all of its immediate subclasses in the next level below that, then each of their immediate subclasses in the next level below that, and so on. Indeed, before you write a line of your own code, Cocoa already consists of exactly such a vast repertoire of classes arranged in exactly such a hierarchical relationship. Xcode will actually display this relationship for you: choose View → Navigators → Symbol and click Hierarchical, with only the second icon in the filter bar darkened (Figure 4-1). (In Xcode 3.2.x, choose Project → Class Browser and switch to “Hierarchy, all classes.”)
The reason for the class–subclass relationship is to allow related classes to share functionality. Suppose, for example, we have a Dog class and a Cat class, and we are considering defining a
walk method for both of them. We might reason that both a dog and a cat walk in pretty much the same way, by virtue of both being quadrupeds. So it might make sense to define
walk as a method of the Quadruped class, and make both Dog and Cat subclasses of Quadruped. The result is that both Dog and Cat can be sent the
walk message, even if neither of them has a
walk method, because each of them has a superclass that does have a walk method. We say that a subclass inherits the methods of its superclass.
The purpose of subclassing is not merely so that a class can inherit another class’s methods; it’s so that it can define methods of its own. Typically, a subclass consists of the methods inherited from its superclass and then some. If Dog has no methods of its own, it is hard to see why it should exist separately from Quadruped. But if a Dog knows how to do something that not every Quadruped knows how to do — let’s say, bark — then it makes sense as a separate class. If we define
bark in the Dog class, and
walk in the Quadruped class, and make Dog a subclass of Quadruped, then Dog inherits the ability to walk from the Quadruped class and also knows how to bark.
It is also permitted for a subclass to redefine a method inherited from its superclass. For example, perhaps some dogs bark differently from other dogs. We might have a class NoisyDog, for instance, that is a subclass of Dog. Dog defines
bark, but NoisyDog also defines
bark, and defines it differently from how Dog defines it. This is called overriding. The very natural rule is that if a subclass overrides a method inherited from its superclass, then when the corresponding message is sent to an instance of that subclass, it is the subclass’s version of that method that is called.