The Objective C Language

Contents

Introduction

Objective C is an extension to the C language. It's designed to give C a full capability for object-oriented programming, and to do so in a simple and straightforward way. Its additions to C are few and are mostly based on Smalltalk, one of the first object-oriented programming languages.

Objective C syntax is a superset of standard C syntax, and its compiler works for both C and Objective C source code. The compiler recognizes Objective C source files by a ª.mº extension, just as it recognizes files with only standard C syntax by a ª.cº extension. As implemented for NeXTSTEP, the Objective C language is fully compatible with ANSI standard C.

Objective C can also be used as an extension to C++. At first glance, this may seem superfluous since C++ is itself an object-oriented extension of C. But C++ was designed primarily to be ªa better C,º and not necessarily as a full-featured object-oriented language. It lacks some of the possibilities for object-oriented design that dynamic typing and dynamic binding bring to Objective C. At the same time, it has useful language features not found in Objective C. When you use the two languages in combination, you can assign appropriate roles to the features found in each and take advantage of what's best in both.

Because object-oriented programs postpone many decisions from compile time to run time, object-oriented languages depend on a run-time system for executing the compiled code. This paper touches on important elements of the run-time system as they're important for understanding language features. NeXT has modified the GNU C compiler to also compile Objective C and provides its own run-time system.

Objects

As the name implies, object-oriented programs are built around objects. An object associates data with the particular operations that can use or affect that data. These operations are known as the object's methods; the data they affect are its instance variables. In essence, an object bundles a data structure (instance variables) and a group of procedures (methods) into a self-contained programming unit.

For example, if you were to write a program that modeled home water usage, you might need to invent a kind of Faucet object that would have methods to start and stop the flow of water, set the rate of flow, return the amount of water consumed in a given period, and so on. (Clearly, a programmatic faucet can be smarter than a real one.) To do this work, a Faucet object would need instance variables to keep track of whether the tap is open or shut and how much water is being used.

One of the objects defined in the NeXTSTEP Application Kit can display a matrix of cells on-screen. The cells might be text fields where the user can enter data, a series of mutually exclusive switches, a list of buttons or menu commands, or a bank of sliders. A Matrix object contains instance variables that define the matrix, including its dimensions and coordinates, the font used to display character strings in the cells, the arrangement of cells into rows and columns, and what to do when a cell is selected. A Matrix also has methods that do such things as alter its size, change its position on-screen, add and remove cells, set the color that's displayed between cells, track the cursor over the cells, and highlight the user's selection.

In Objective C, an object's instance variables are internal to the object; you get access to them only through the object itself. For others to find out something about the object, there has to be a method to supply the information. Methods wrap around the object's instance variables and hide them from the rest of the program.

Moreover, an object sees only the methods that were designed for it; it can't mistakenly perform methods intended for other types of objects. Just as a C function localizes its automatic variables, hiding them from the rest of the program, an object hides both its instance variables and its method implementations.

Object Networks

A program consists of a network of interconnected objects that call upon each other to solve a part of the puzzle. Each object has a specific role to play in the overall design of the program.

For example, in addition to Faucets, the program that models water usage might also have WaterPipe objects that can deliver water to the Faucets, a Building object to coordinate a set of WaterPipes and Faucets, some Appliance objectsÐcorresponding to dishwashers, washing machines, and toiletsÐthat can turn Faucets on and off, and some Users to work the Appliances and Faucets. When a Building object is asked how much water is being used, it might call upon each Faucet to report its current state. When a User starts up an Appliance, the Appliance will need to turn on a Faucet to get the water it requires.

Similarly, each cell in a Matrix is a kind of Cell object that responds to orders from the Matrix. When a Matrix object is asked to display the matrix on-screen (in effect, when it's asked to display itself), it, in turn, tells each Cell object to draw the contents of the cell. Before it can draw, a Cell may need to request information from still other objects.

To communicate with each other in this way, objects in a program must know about each other. Typically, an object has instance variables that store the addresses of other objects. These instance variables serve as outlets for the messages it will send. A Faucet might record which WaterPipe it's connected to. A Matrix keeps a list of its Cells. An object knows only about the objects it must communicate with to fulfill its own role in the program. A Matrix needs to know what its Cells are, but the Cells don't need to know which Matrix they're in.

These interconnections define the structure of the application. They link objects into a communicating network, much as individuals are linked by their patterns of social relationsÐor as the components of a water system are linked by their physical connections. Object-oriented programs are well-suited for modeling the interactions of components in a complex system.

The object network is set into motion by an external stimulus. If you're writing an interactive application with a user interface, it will respond to user actions on the keyboard or mouse. A program that tries to factor very large numbers might start when you pass it a target number on the command line. Other programs might respond to data received over a phone line, information obtained from a database, or information about the state of a mechanical process the program monitors.

id

In Objective C, objects are identified by a distinct data type, id. This type is defined as a pointer to an objectÐin reality, a pointer to the object's data structure (its instance variables). Like a C function or an array, an object is identified by its address. All objects, regardless of their instance variables or methods, are of type id.

id anObject;

For the object-oriented constructs of Objective C, such as method return values, id replaces int as the default data type. (For strictly C constructs, such as function return values, int remains the default type.)

The keyword nil is defined as a null object, an id with a value of 0. id, nil, and the other basic types of Objective C are defined in the header file objc.h, which is located in the objc subdirectory of /NextDeveloper/Headers.

Dynamic Typing

The id type is completely nonrestrictive. By itself, it yields no information about an object, except that it is an object.

But objects are not all the same. A Faucet won't have the same methods or instance variables as a Building or an Appliance. A Matrix isn't the same as one of its Cells. At some point, a program needs to find more specific information about the objects it usesÐwhat the object's instance variables are, what methods it can perform, and so on. Since the id type designator can't supply this information to the compiler, each object must be able to supply it at run time.

This is possible because every object carries with it an isa instance variable that identifies the object's classÐwhat kind of object it is. Every Faucet object would be able to tell the run-time system that it is a Faucet. Every Matrix can say that it is a Matrix.

Objects are thus dynamically typed at run time, rather than statically typed at compile time. This has far-reaching consequences for object-oriented design, and is the foundation for dynamic binding, discussed below.

Object classes are discussed in more detail under ªClassesº below.

Note: It's also possible to give the compiler information about the class of an object by statically typing it in source code using the class name. Classes are particular kinds of objects, and the class name can serve as a type name. See ªClass Typesº and ªStatic Optionsº later in this paper.

Messages

to get an object to do something, you send it a message telling it to apply a method. In Objective C, message expressions are enclosed in square brackets:

[receiver message] The receiver is an object, and the message tells it what to do. In source code, the message is simply the name of a method and any arguments that are passed to it. When a message is sent, the run-time system selects the appropriate method from the receiver's repertoire and invokes it. For this reason, the method name in a message is called a method selector.

For example, this message tells the myMatrix object to perform its display method, which draws the matrix and its cells in a window:

[myMatrix display];

Methods can also take arguments. The message below tells myMatrix to change its location within the window to coordinates (30.0, 50.0):

[myMatrix moveTo:30.0 :50.0];

Here the method name, moveTo::, has two colons, one for each of its arguments. The arguments are inserted after the colons, breaking the name apart. Colons don't have to be grouped at the end of a method name, as they are here. Usually a keyword describing the argument precedes each colon. The getRow:andColumn:ofCell: method, for example, takes three arguments:

int row, column;
[myMatrix getRow:&row andColumn:&column ofCell:someCell];
This method finds the location of someCell in the matrix and puts the row and column where it's located in the two variables provided.

Methods that take a variable number of arguments are also possible. Extra arguments are separated by commas after the end of the method name. (Unlike colons, the commas aren't considered part of the name.) In the following example, the imaginary makeGroup: method is passed one required argument (group) and three that are optional:

[receiver makeGroup:group, memberOne, memberTwo, memberThree];

Like standard C functions, methods can return values. The following example assigns the identifying integer returned by the tag method to a variable also named tag.

int tag; tag = [myMatrix tag];

Note that a variable and a method can have the same name.

A message to nil also is valid,
[nil moveTo:100.0 :22.5];
but it has no effect and makes little sense. Messages to nil simply return nil.

The Receiver's Instance Variables

A method has automatic access to the receiving object's instance variables. You don't need to pass them to the method as arguments. For example, the tag method illustrated above takes no arguments, yet it can find the tag for myMatrix and return it. Every method assumes the receiver and its instance variables, without having to declare them as arguments.

This convention simplifies Objective C source code. It also supports the way object-oriented programmers think about objects and messages. Messages are sent to receivers much as letters are delivered to your home. Message arguments bring information from the outside to the receiver; they don't need to bring the receiver to itself.

A method has automatic access only to the receiver's instance variables. If it requires information about a variable stored in another object, it must send a message to the object asking it to reveal the contents of the variable. The tag method is used for just this purpose.

Polymorphism

As the examples above illustrate, messages in Objective C appear in the same syntactic positions as function calls in standard C. But, because methods ªbelong toº an object, messages behave differently than function calls.

In particular, an object has access only to the methods that were defined for it. It can't confuse them with methods defined for other kinds of objects, even if another object has a method with the same name. This means that two objects can respond differently to the same message. For example, each kind of object sent a display message could display itself in a unique way. A Faucet and a Building could respond differently to a waterUsed message.

This feature, which plays a significant role in the design of object-oriented programs, is sometimes referred to as polymorphism. Together with dynamic binding, it permits you to write code that might apply to any number of different kinds of objects, without your having to choose at the time you write the code what kinds of objects they might be. They might even be objects that will be developed later, by other programmers working on other projects. If you write code that sends a display message to an object, any object that has a display method is a potential receiver.

Dynamic Binding

A crucial difference between function calls and messages is that a function and its arguments are bound together in the compiled code, but a message and a receiving object aren't united until the program is running and the message is sent.LI> Defining a Class

Therefore, the exact method that will be invoked to respond to a message is determined at run time, not when the code is compiled. The method that's used depends on the receiver, and different receivers may have different method implementations for the same method selector (polymorphism). For it to find the right method implementation for a message, the compiler would have to have precise information about the receiver. This is information the receiver is willing to reveal at run time when it receives a message (dynamic typing), but it's not available from the type declarations found in source code.

When a message is sent, a run-time messaging routine looks at the receiver and at the method selector in the message. It locates the receiver's implementation of a method matching the selector, ªcallsº the method, and passes the receiver's instance variables to it. (For more on this routine, see ªHow Messaging Worksº below.)

This dynamic binding of methods to messages works hand-in-hand with polymorphism to give object-oriented programming much of its flexibility and power. Since each object can have its own version of a method, a program can achieve a variety of results, not by varying the message itself, but by varying just the object that receives the message.

This can be done as the program runs. Receivers can be decided ªon the flyº and can be made dependent on external factors such as user actions. In the Application Kit, for example, users determine which objects receive messages from menu commands like Cut, Copy, and Paste. The message goes to whatever object controls the current selection. An object that displays editable text would react to a copy: message differently than an object that displays scanned-in images. A Matrix would respond differently than a Cell. Since methods aren't bound to messages until run time, these differences are isolated in the methods that respond to the message; the code that sends the message doesn't have to be concerned with them. Each application can invent its own objects that respond in their own way to a copy: message.

It's taken from a documentation of Release 3.0 NeXT Computer, Inc. All Rights Reserved.