Classes
Objects in Objective-C are defined in terms of a class. New classes of objects are specializations of a more general class. Each new class is the accumulation of the class definitions that it inherits from and can expand on that definition by adding new methods and instance variables or redefining existing methods to perform new or expanded functionality. Like Java and Smalltalk, but unlike C++, Objective-C is a single inheritance language , meaning that a class can inherit functionality only from a single class.
A class is not just a blueprint for building objects; it is itself an object in the runtime that knows how to build new objects. These new objects are instances of the class.
The Root Class
Every class hierarchy begins with a root
class
that has no superclass. While it is
possible to define your own root class in Objective-C, the classes
you define should inherit, directly or indirectly, from the
NSObject
class provided by the Foundation
framework. The NSObject
class defines the behavior
required for an object to be used by the Cocoa framework and provides
the following functionality:
Defines the low-level functionality needed to handle object initialization, duplication, and destruction.
Provides mechanisms to aid Cocoa’s memory management model.
Defines functionality for an object to identify its class membership and provide a reasonable description of the object.
Defining a Class
In Objective-C, classes are defined in two parts, usually separated into two different files:
An interface, which declares a class’s methods and instance variables, and names its superclass. The interface is usually specified in a file with the .h suffix typical of C header files.
An implementation , which contains the code that defines the class’s methods. By convention, files containing the implementation of a class have a .m suffix.
The interface
To declare a class and give all the information other classes (and other programs) need to use it, an interface file needs to contain the following information:
The class that is being inherited from
The instance variables, if any, that the class adds
A list of method declarations, if any, indicating what methods the class adds or modifies significantly
Example 1-1 shows simple header file, saved by
convention as Song.h, containing the interface
for the Song
class.
#import <Cocoa/Cocoa.h> // 1 @interface Song : NSObject { // 2 id title; // 3 } - (id)title; // 4 - (void)setTitle:(id)aTitle; // 5 @end; // 6
Each line is defined as follows:
Imports the definitions for the Cocoa frameworks. This line is similar to the
#include
directive in C, except the compiler ensures that it doesn’t include a header file more than once.Declares the name of the class,
Song
, and specifiesNSObject
as its superclass.Declares an instance variable named
title
. Theid
type indicates that the variable is an object. If we wanted the compiler to enforce type checking for us, we could declare its type asNSString *
.Declares an instance method named
title
that returns an object. The - (minus sign) before the method name indicates that the method is an instance method.Declares an instance method named
setTitle
that takes an object argument and doesn’t return anything.The
@end;
statement indicates to the compiler the end of theSong
class interface.
Scoping instance variables
The object-oriented principle of encapsulation means that other programmers shouldn’t need to know a class’s instance variables. Instead, they need to know only the messages that can be sent to a class. The inclusion of instance variables in the interface file, while required by C, would seem to break encapsulation.
To give a class the ability to enforce encapsulation even though the variables are declared in the header file, the compiler limits the scope of the class’s instance variables to the class that declares them and its subclasses. This enforcement can be changed by using the following set of compiler directives:
-
@private
These instances are accessible within the class from which they are declared. Subclasses will not be able to access them.
-
@protected
These instances are available within the class that declares them and within classes that inherit from them. This is a variable’s default scope.
-
@public
These instances are available to any class and can be used by code as if they were a field in a C structure. However, the directive should not be used except when absolutely necessary, because it defeats the purpose of encapsulation.
For example, to ensure that subclasses of the Song
class could not directly access the title instance variable, use the
@private
directive as shown in Example 1-2.
The implementation
To define how the class works,
an
implementation file needs to contain implementations of the methods
defined in the interface file. Example 1-3 shows the
implementation, contained in the source file Song.m
by convention, of the Song
class.
Here is a detailed explanation of each part of this code:
Imports the header file that contains the interface for the file. Every implementation must
import
its own interface.Declares that what follows is the
implementation
of theSong
class.Implementation of the
title
method. This method simply returns thetitle
variable’s value. The contents of a method are defined, like C functions, between a pair of braces. Also, the class’s instance variables are in the scope of the method and can be referred to directly.Implementation of the
setTitle
method. This method sets thetitle
variable to theaTitle
argument after performing some steps, using theretain
andautorelease
messages required for proper memory management. For more information about memory management, see Section 1.5, later in this chapter.Indicates to the compiler the end of the
Song
class implementation.
Notice that the implementation doesn’t need to repeat the superclass name or the instance variable declarations.
Special Variables
In addition to a class’s instance variables, several other instance variables are defined within the scope of instance methods. These variables are:
-
isa
Defined by the
NSObject
class, theisa
variable contains a pointer to the class object. This lets an object introspect itself. It is also what lets the runtime determine what kind of object it is when it resolves messages to methods.-
self
A variable set by the runtime to point at the object the action is performed on—the receiver object of the message. This allows the functionality within a method to send messages to the object on which the method acts.
-
super
A variable set by the runtime that behaves similarly to self, except that the resolution of message to method starts with the object’s superclass. This allows you to call the functionality of superclasses.
-
_cmd
The selector used to call the current method.
Class Methods
Since classes are objects, you can define methods that will act when
messages are sent to a class. Class methods are defined in the same way
as instance methods, except you use a plus symbol
(+
) at the beginning of the method declaration
instead of a hyphen or minus sign (-). For example, if the
Song
class keeps track of the number of songs
created, a numberOfSongs
class method could be
provided, as shown in Example 1-4.
#import <Cocoa/Cocoa.h>
@interface Song : NSObject {
id title;
}
+ (int)numberOfSongs;
- (id)title;
- (void)setTitle:(id)aTitle;
@end;
Similarly, this method’s implementation is placed
between the @implementation
and
@end
directives in the implementation
(.m) file. Since a class method operates on the
class object, the isa
, self
,
super
, and _cmd
variables are
defined the same way as instance variables.
Overriding Superclass Methods
When a new class is defined, a method can
be implemented with the same name as a method in one of the
superclasses up the inheritance hierarchy. This new method overrides
the original when messages with the method name are sent to the
derived class’s object. When overriding methods, you
can access the superclass’s method functionality by
sending a message to the special variable super
.
For example, if the class of iPod
inherits from a
more generic MP3Player
class that also defines the
play
method, the subclass’s
play
method may require that the superclass
functionality is executed. Example 1-5 shows how
this could be achieved by using the super
variable.
When a superclass method is overridden, the method doesn’t need to be declared again in the interface (.h) file. By convention, an overridden method is listed in the interface file only if you significantly change the way the method works.
Get Cocoa in a Nutshell 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.