In object-oriented languages,
an object encapsulates attributes and provides methods. These methods
can be used by the outside world (i.e., other objects) to change the
object's state as well as to interact with the object. All this can be
achieved without opening the actual implementation of the object's
behavior to the outside world.
In Objective-C, in order to create a new class, you first need to declare it using an interface and then define it using an implementation.
The declaration and the definition are usually written in two separate
files. The declaration part (described more fully in the following
sections) is customarily done in a .h file having the same name as the class, while the implementation, which also has the same name as the class, is in a .m file. Both the declaration and the definition parts use compiler directives. A compiler directive is an instruction to the Objective-C compiler prefixed by the @ sign. The declaration is signaled to the compiler using the @interface directive, while the actual definition is signaled using the @implementation directive.
1. Class declaration
To declare a class, MyClassName, as a subclass of class MyParentName, you simply write:
@interface MyClassName : MyParentName{
// attribute declarations
}
// method declarations
@end
Here, we are telling the compiler that a new class type, MyClassName, is being declared. MyClass-Name is a subclass of MyParentName
class. In addition, we list the definition of all instance variables
between the curly brackets. The methods are declared between the end of
the curly bracket and the @end compiler directive.
There are a few important aspects of the @interface declaration:
The attributes declared between the curly brackets are instance variables. At runtime, every class has a unique class object and zero or more instances of the class. Every instance (object) of MyClassName has its own values for these attributes. The unique class object has no access to these instance variables.
Methods declared can be either instance methods or class methods. An instance method is called by sending a message
to an actual instance (i.e., an object) of the class. A class method
does not require a class instance. You call a class method by sending a
message to the unique class object. In Objective-C, every class has
exactly one class object during the runtime of the program. An instance
method is declared/defined by a "-" prefix, while a class method is
declared/defined by a "+"prefix.
For example:
-(Address *) getAddress;
is an instance method, while
+(id) getANewInstance;
is a class method.
Objective-C does not support class variables. However, you can use the familiar static
keyword in an implementation file of a given class. This will allow
instance methods (i.e., those with a "-" prefix in their definition) to
have access to the single value of this variable shared by all instances
of that declared class. If you define a static variable inside a method, then that method is the only method that has access to that variable. If you put the definition of the static variable outside the class implementation, then all methods have access to that variable.
2. How do I use other declarations?
As a Cocoa developer, you will need to be able to use
classes that other developers have written. In addition, if the
declaration and the definition of your classes are in separate files,
you will need to inform the compiler about the location of the class
declaration in the implementation file.
If you use the name of a class without accessing its methods or instance variables, you can just use the @class directive. This gives the compiler enough information to successfully compile the code. Usually the @class directive is used in class declarations. For example, consider the following declaration:
@class Address;
@interface Person{
Address *address;
}
@end
Here, we have a Person class declaration that uses the Address class. The compiler only needs to know that the Address is a class type. No details about the actual methods and attributes are needed since we just use the type.
If, on the other hand, you use the methods and/or the
attributes of a given class, then you need to point the compiler to the
location of the file that contains the declaration. There are two ways
to do that: (1) using #include, and (2) using #import. #include and #import are almost identical, except that #import loads the given file only once during the compilation process. The #import directive is much simpler, fully supported by Apple, and produces potentially fewer problems. Bottom line: use #import.
3. Class definition
To actually define a class, you need to specify the actual implementation of the class/instance methods declared in the @interface part. To define the class, you write:
#import "MyClassName.h"
@implementation MyClassName
// method definitions
@end
Notice that we needed to import the declaration file.
This import allowed us to skip repeating the parent's class name as
well as the instance variables. Both can be deduced by the compiler so
there is no need to repeat them.
4. Method invocation and definition
Method invocation in Objective-C addresses a problem
that those of you who have used Java will be familiar with. In Java, a
method is invoked using its name followed by a pair of left and right
parentheses. If the method requires parameters, the values of these
parameters are inserted inside the parentheses and separated by commas.
For example, if aPoint is a Java object representing a point in 3D, setLocation (float x, float y, float z) can represent a method for changing the location of this point object. aPoint.setlocation(3, 6, 9) asks the aPoint
object to change its location to (3, 6, 9). One problem with this
notation is readability. If you come across such a statement written by
another programmer, you cannot know for sure what these values
represent. You have to go to the interface of this class and read about
what each position in the parameters list represents.
Because Objective-C is an object-oriented language, it, too, provides data encapsulation. With data encapsulation, the outside world interacts with an object by sending messages to that object. To send an object (aObject) a message (aMessage) you use square brackets and write [aObject aMessage].
A message is composed of two parts: (1) keywords, and (2) parameters.
Every message has at least one keyword. A keyword is an identifier
followed by a colon.
Let's make these definitions concrete by writing the setlocation method invocation in Objective-C. In Objective-C, you write something like:
[aPoint setLocationX:3 andY:6 andZ:9];
Notice the improved readability of the invocation of
the method. Just by looking at it, we know that 6 is used to change the
y-coordinate. This message has three keywords: setlocationX:, andY:, andZ:. The method is represented by setLocationX:andY:andZ:. This representation is called a selector.
A selector is a unique name (within a class) of a method used by the
runtime to locate the code implementing that method. The method is
declared in the interface as:
-(void)setLocationX:(float) x andY:(float) y andZ:(float) z;
The statement [aPoint setLocationX:3 andY:6 andZ:9] as a whole is called a message expression.
If this expression evaluates to an object, then it, too, can receive a
message. Objective-C allows nested message invocation. For example, you
can write:
[[addressBook getEntryAtIndex:0] printYourself];
First, the message getEntryAtIndex:0 is sent to the addressBook object. The method identified by the selector getEntryAtIndex: returns an object. This object is then sent a printYourself message.
It's worth noting that if a method has zero
parameters, you should not use the ":" when the method is invoked. This
notation can be difficult to deal with at first, but after a time it
becomes natural.
Methods in Objective-C are always public. There is no such thing as a private method. Instance variables are defaulted to protected, a setting that works well for you most of the time.
5. Important types
We mentioned before that every class in a Cocoa application has a singleton class object. The type of this class object is Class. A null class pointer is of type Nill. Nill is basically (Class)0. We also learned that a class can be instantiated. An instance of a class A is declared as:
A *anObject;
There is, however, a defined type in Cocoa that represents an arbitrary object. This type is named id. If anObject does not point to any object, its value is nil. A nil is basically (id)0.
SEL is a defined type that represents a selector. To obtain the SEL of a method, aMethod:, use the directive @selector as follows.
SEL mySelector = @selector (aMethod:);
If you want mySelector to point to a null selector, assign it NULL.
You can also obtain the SEL of a method from a string representation of its name. The function to use is NSSelectorFromString() which is declared as:
SEL NSSelectorFromString (NSString *aSelectorName);
NSSelectorFromString will always return a SEL named by a non-nil aSelectorName even if the selector is not found. If there is no selector with the name aSelectorName, a new selector is registered with this name and returned. If the parameter aSelectorName is nil or the function faces memory problems, it will return NULL (basically (SEL)0).
6. Important Cocoa classes
There are several important Cocoa classes that you
will often use in your iPhone application.
NSObject. This is the base class of most Cocoa classes. An object is not considered a Cocoa object if it is not an instance of NSObject or any class that inherits from NSObject. This class defines the runtime methods required for allocating and deallocating objects.
NSString.
This is the main class representing strings in Cocoa. Using this class,
you can store an arbitrary text. However, once you store a value in an
object of this type, you cannot change it. This kind of class is
referred to as immutable. To be able to change a string's value (e.g., append text to it, etc.), you need the mutable string class NSMutableString. You can create a constant string using the "@" sign. For example, @"Plano" represents an NSString instance.
NSArray. Instances of this class represents Cocoa array objects. The mutable version of this class is NSMutableArray.
NSSet. Instances of this class represents Cocoa set objects. The mutable version is NSMutableSet.