MacDevCenter    
 Published on MacDevCenter (http://www.macdevcenter.com/)
 See this if you're having trouble printing code examples


Programming With Cocoa

The Objective-C Language

05/04/2001

Last time, we talked about the major concepts of object-oriented programming. Today, I want to tell you how Objective-C implements these concepts in the language, as well as show you what Objective-C code actually looks like.

The most fundamental ability we must have in object-oriented programming is the ability to send messages to objects. To send a message to an object in Objective-C, we use the following construct:

[receiver message]

The receiver is the object you are sending the message to, and message is the method you wish the receiving object to perform. For example, suppose we have an object dataTable in our program whose data we want to update. To do this, we would have the following line of code in our program which is a message to the table:

[dataTable reloadData];

Arguments in Objective-C methods are indicated by a colon.

[textField setEditable:YES];

You can have any number of arguments, each preceded by a colon. Messages can also be nested within each other, as long as the argument types and return types agree. For example, we could read the value from a slider control and then display it in a text field.

[textField setDoubleValue:[slider doubleValue]];

Here we see that [slider doubleValue] is the argument of [textField setDoubleValue: ]. The doubleValue command returns a type double number, which is the data type of the argument of setDoubleValue.

Speaking of data types, objects in Objective-C are all given the data type id. Variables of type id are nothing more than object identifiers. They are actually just pointers to the object's data structure; this is beyond the scope of our discussion now, however. In code, we would create variables that refer to objects like any other variable.

id anObject;

In Objective-C, the default return value data type is id. So, if you have a method that has no return value data type, then it will automatically be given type id.

Comment on this articleNow that we've waded knee-deep into Objective-C programming, how is the column working for you?
Post your comments

It's also possible to type a data variable to a specific class. This is known as static typing. All variables that refer to objects are really pointers to their memory location. This implementation is pretty transparent to the programmer in most places. However, when it comes to static typing, it is not so transparent.

When we create a variable of type id the fact that it is a pointer to an object is implicit in the typing. id is by definition a pointer type -- an object identifier. But if you have a variable that refers to a string, and you want to statically type it to NSString, you have to make it explicit in code that the variable is a pointer. This is done by using the C pointer declaration syntax:

NSString *aString;

Learning CocoaLearning Cocoa
By Apple Computer, Inc.
Table of Contents
Index
Sample Chapter
Full Description
Read Online -- Safari

The astrisk in front of the variable name is not part of the name, but rather indicates that aString is a pointer to some NSString object. This is the only place you need to be aware of the pointer nature of object identifier variables. Only when declaring new variables do you need to use the C pointer construct. Whenever you subsequently refer to aString, it will be as any other variable.

If you're not comfortable with pointers, or C itself check out The C Programming Language by Brian Kerrnighan and Dennis Ritchie (they actually wrote the C language). I also like and recommend Practical C Programming by Steve Oulline.

Options for creating nested methods

Now that we have a data type for objects, we can create nested methods in another way than above, where a message with return value type id can be the receiver of another message.

double number;
number = [[anArray lastObject] doubleValue];

Of course we're assuming here that the object returned by [anArray lastObject] does indeed respond to the doubleValue message we send to it above. What is happening is that we have an array of objects. The message [anArray lastObject] returns the object in the array with the highest index, which is then sent a doubleValue message. Alternatively, we can code this in a more verbose fashion:

id tempObject;
double number;
tempObject = [anArray lastObject];
number = [tempObject doubleValue];
Defining Classes

Defining new classes in Objective-C

Now I want to talk to you about how new classes are defined in Objective-C source code. In the true spirit of the separation of interface from implementation, Objective-C defines new classes in two separate files -- an interface file and an implementation file.

The interface contains all the information required to use the class, including declarations of all instance variables and definitions of all the methods. Programmers using classes look to the interface when they want to know what a class is capable of doing. The implementation file actually contains the source code that defines how the methods are implemented.

The interface and the implementation of a class are generally separated into two files, although this practice is not mandated by the compiler. Implementation files are suffixed by a .m, which is the extension for all Objective-C source files. The interface file has the extension .h, which is typical of header files in C. Class file names typically carry the same name as the class, although this too is not required by the compiler. Thus a hypothetical "Circle" class would be defined in the files Circle.h and Circle.m.

The interface file

The interface file declares what instance variables and methods make up a class. The structure of an interface file is:

@interface ClassName : ItsSuperclass
{
	instance variable declerations
}
method declerations
@end

Interface declarations always begin with the @interface compiler directive and end with the @end compiler directive. Following the @interface directive on the first line is the class name. A colon follows the class name and after that is the class from which your new class inherits -- its superclass. If you fail to link your class to a superclass, then it is assumed that you are creating a new root class, such as NSObject in Cocoa.

Instance variable declarations follow the first line enclosed in braces. These are the data structures of the class that the methods have to work with. For our hypothetical Circle class the variable declarations might be

double radius;
double xLocation;
double yLocation;
NSColor *color;

Instance variable declarations are always preceded by the data type. NSColor, as we will learn later, is an Application Kit class that allows us to work with colors.

Following the instance variable declarations are the method declarations. Like standard C functions, methods in Objective-C have a return type and can take arguments. Additionally, methods can either be class methods or instance methods. Class methods can only be performed by the class object discussed above, and instance methods can be performed by any object of the class. A plus sign before the method name indicates that the method is a class method.

+ alloc

Method declarations that begin with a minus sign are instance methods

- (void)setXLocation:(double)x Ylocation:(double)y;

The return type of the method is enclosed in parentheses before the actual method name. Arguments follow colons and multiple arguments are separated by the argument name and a space. Like method return types, argument data types are also enclosed in parentheses before the name of the argument.

For a class to link to a superclass you must import the superclass's interface file, which usually is the superclass name followed by a .h.

#import "ItsSuperclass.h"

The #import statement serves the exact same purpose as the #include statement many of you are probably familiar with from C -- only it's smarter. The key advantage to #import is that it knows if a file has already been included, and will not bring in duplicates; #include does not check for this.

The implementation file

The implementation file is where the meat of your class is coded. This file contains all the source code that gives your methods substance -- that allows them to do something useful. Remember now that the interface file is where your class is described, and this is the description programmers are to use to incorporate your class into their code. The implementation file is where you do your work.

The implementation file follows a similar format as the interface file, as shown below.

#import "ClassName.h"

@implementation ClassName : ItsSuperclass
{
	instance variable declarations
}
method definitions
@end

Every class implementation must import its own interface file. Because the interface declares the supeclass and the superclass from which your class inherits, these things can be omitted from the implementation file. This reinforces the concept of the implementation being purely devoted fleshing out the methods. Thus, in practice, the implementation file only need contain the following code:

#import "ClassName.h"

@implementation ClassName
method definitions
@end

Methods are defined much like C functions are defined. The name is written exactly as is done in the interface file (without the semicolon), and method implementation code is contained in a pair of braces following the name. For example, our Circle class might have the following methods:

+ alloc
{
	your code
}

- (void)setXLocation:(double)x yLocation:(double)y
{
	your code
}

You may be wondering why + alloc has not returned the data type. The default return data type in Objective-C is type id -- that is, an object is returned by default. The + alloc method is designed to return an object of the class, and thus no return type need be specified. While we're on the topic of the alloc method, I should mention that you will rarely need to implement + alloc on your own. The NSObject class takes care of this. + alloc's purpose is to allocate (hence the name) memory space for newly created objects. The following sections will go into more detail about creating new objects.

Instance variables can be referred to in your method definitions directly. All instance variables are within the scope of each method without explicitly saying so. They are analagous to global variables in C. Thus, we could simply define the second method above as follows:


- (void)setXLocation:(double)x yLocation:(double)y
{
	xLocation = x;
	yLocation = y;
}
It is also possible to have local variables with more limited scope within each method. For example, in the second method above, we could insert a useless intermediary variable:

- (void)setXLocation:(double)x yLocation:(double)y
{
	double tempX;
	double tempY;

	tempX = x;
	tempY = y;

	xLocation = tempX;
	yLocation = tempY;
}

If I may go out on a limb, you could say that anything that applies to standard C functions, also applies to methods of a class.

Creating new objects

New objects are created by sending an alloc message to the class object of the class you wish to make an instance of. For example, if we wanted to create an instance of the Circle class, we would do the following:


id aCircle;
aCircle = [Circle alloc];

Remember, alloc returns an object, so the variable that refers to the instance you're creating must also be of type id. Once a new instance has been created we must also initialize its instance variables. This is done by issuing an init message to the newly created object.


[aCircle init];

Initialization must occur directly after allocation, so it's more common to create new objects by nesting the two messages above.


aCircle = [[Circle alloc] init];

By default, init initializes all the instance variables to zero. You are free to create your own initialization methods, called constructors, to initialize the variables as you see fit. By default constructors begin with "init". Because a constructor must interact with instance variables of an object, it must be an instance method, rather than a class method like alloc. For example, you might have a method in our circle class to initialize the circle with a radius of 10. This method would look like this:


- (void)initWithRadius:(double)r;
{
	radius = r;
}

Any instance variables not initialized in your custom constructor will be set to zero by default.

In Project Builder

Also in Programming With Cocoa:

Understanding the NSTableView Class

Inside StYNCies, Part 2

Inside StYNCies

Build an eDoc Reader for Your iPod, Part 3

Build an eDoc Reader for your iPod, Part 2

I haven't talked about Project Builder just yet, but I just wanted to touch on a feature that relates to the current discussion. Project Builder contains templates for class interface and implementation files. What you get is a skeleton of a class that inherits from NSObject in the Foundation Framework -- all you have to do is flesh it out with your data structures and methods. So, now you both understand the struture of a class in source code, and how to make one with less work.

It won't be long now before we finish up with the nuts and bolts and get into programming real applications. Next time, I will introduce you to Interface Builder. Our foray into Interface Builder will be largely experiential. In the next three columns, I will go through two complete applications where you will see how Interface Builder and Project Builder work together to make your programming more enjoyable. See you then!

Copyright © 2009 O'Reilly Media, Inc.