Important: The Java API for Cocoa is deprecated in Mac OS X version 10.4 and later. You should use the Objective-C API instead. For a tutorial on using Cocoa with Objective-C, see Cocoa Application Tutorial.
Interface Builder is a versatile tool for application developers. It enables you to not only to compose the application’s graphical user interface, but it gives you a way to define much of the programmatic interface of the application’s classes and to connect the objects eventually created from those classes.
The following sections show how to define the ConverterController class and connect it to Currency Converter’s user interface.
Classes and Objects
Specify the ConverterController Class
Paths for Object Communication: Outlets, Targets, and Actions
Define the User Interface and Model Outlets of the ConverterController Class
Define the Actions of the ConverterController Class
To newcomers, explanations of object-oriented programming might seem to use the terms “object” and “class” interchangeably. Are an object and a class the same thing? And if not, how are they different? How are they related?
An object and a class are both programmatic units. They are closely related, but serve quite different purposes in a program.
First, classes provide a taxonomy of objects, a useful way of categorizing them. Just as you can say that a particular tree is a pine tree, you can identify a particular software object by its class. You can thereby know its purpose and what messages you can send it. In other words, a class describes the type of an object.
Second, you use classes to generate instances of them—or objects. Classes define the data structures and behavior of their instances, and at run time create and initialize these instances. In a sense, a class is like a factory, stamping out instances of itself (objects of its class) when requested.
What especially differentiates a class from its instance is data. An instance has its own unique set of data, but its class, strictly speaking, does not. The class defines the structure of the data its instances have, but only instances can hold data. The class also implements the behavior of all its instances in a running program.
Implicit in the notion of a taxonomy is inheritance, a key property of classes. Classes exist in a hierarchical relationship to one another, with a subclass inheriting behavior and data structures from its superclass, which in turn inherits from its superclass.
You must go to the Classes pane of the nib file window to define a class. To design the ConverterController class:
In the MainMenu.nib window, click Classes.
In the leftmost column of the browser, select java.lang.Object and press Return to create a java.lang.Object subclass called MyObject.
Type ConverterController to rename MyObject, and press Return. Figure 1-16 shows the result of this operation.
In Interface Builder, you specify the paths for messages traveling between the ConverterController object and other objects as outlets and actions.
An outlet is an instance variable that identifies an object. Figure 1-17 illustrates how an outlet in one object points to another object.
Objects can communicate with other objects in an application by sending messages to outlets.
An outlet can reference any object in an application: user-interface objects such as text fields and buttons, windows and dialogs, instances of custom classes, and even the application object itself. What distinguishes outlets is their relationship to Interface Builder.
Outlets are declared as:
public Object variableName;
You might notice that though the outlet type is labeled id in Interface Builder, the variable in the source file is declared with the type Object. This is simply a consequence of the Java specification—since Java is a strongly typed language, the object cannot directly act as a Cocoa id object (which represents a dynamically typed object).
Since Object refers to an arbitrary object type, you can—and should, in most cases—statically type outlets with the appropriate class:
IBOutlet NSButton myButton;
Xcode helps you remember what objects are Interface Builder outlets by appending a comment to the declaration:
IBOutlet NSButton myButton; /* IBOutlet */
This comment is added automatically if the outlet is originally declared in Interface Builder. If you add the outlet explicitly to your class definition, it is good practice to add this comment yourself. It helps you distinguish between interface outlets and other noninterface objects in the future.
Interface Builder can recognize outlets in code by their declarations, and it can initialize outlets. You usually set an outlet’s target in Interface Builder by drawing connection lines between objects. There are ways other than outlets to reference objects in an application, but outlets and Interface Builder’s facility for initializing them are a great convenience.
At application load time, the instance variables that represent outlets are initialized to point to the corresponding target. For example, the rateField of the ConverterController instance would be initialized with a reference to the Exchange Rate text field object (see “Connect the ConverterController Class to the Text Fields” for details). When an outlet is not connected, the value of the corresponding instance variable is null.
It might help to understand connections by imagining an electrical outlet plugged into the destination object. Also picture an electrical cord extending from the outlet in the source object. Before the connection is made, the cord is not plugged in, and the value of the outlet is null; after the connection is made (the cord is plugged in), a reference to the destination object is assigned to the source object’s outlet.
You are free to use all Java language features in your code, but in rare situations, you may need to know something about the Cocoa implementation and its implications for Java code.
The Cocoa classes are implemented in Objective-C. When you use a Cocoa class from Java, you are using a Java “wrapper” class for the Objective-C class of the same name. An instance of such a class is actually an Objective-C instance. In this example, you are using the NSTextField Java class that wraps the Objective-C NSTextField class.
Java uses automatic garbage collection to manage dynamic memory; Objective-C, on the other hand, uses a retain-release reference-counting mechanism that is only semiautomatic. You will almost never have to concern yourself with the interaction between these two mechanisms if you take note of the following advice: Be sure that an object has been assigned to a instance variable before you use it as a target, a delegate, or an NSTableView item.
You can view (and complete) target/action connections in the Connections pane in the Interface Builder inspector. This pane is easy to use, but the relation of target and action in it might not be apparent. First, a target is an outlet of a cell object that identifies the recipient of an action message. Well, you may say, what’s a cell object and what does it have to do with a button?
One or more cell objects are always associated with a control object (that is, an object inheriting from NSControl, such as a button). Control objects “drive” the invocation of action methods, but they get the target and action from a cell. NSActionCell defines the target and action outlets, and most kinds of cells in AppKit inherit these outlets.
For example, when a user clicks the Convert button in the Currency Converter window, the button gets the required information from its cell and invokes the convert method on the target outlet object, which is an instance of the custom class ConverterController. Figure 1-18 shows the interactions between the ConverterController class, the Convert button, and the Amount in Other Currency field.
In the Actions column in the Connections pane in the inspector are all action methods defined by the class of the target object and known by Interface Builder. Interface Builder identifies action methods because their names follow the syntax:
public void myAction(Object sender)
Here, it looks for the argument sender.
Usually the outlets and actions that you connect belong to a custom subclass of java.lang.Object. For these occasions, you need only to follow a simple rule to know which way to specify a connection in Interface Builder. Create the connection from the object that sends the message to the object that receives the message:
To make an action connection, create the connection from an element in the user interface, such as a button or a text field, to the custom instance you want to send the message to.
To make an outlet connection, create the connection from the custom instance to another object (another instance or user-interface element) in the application.
These are only rules of thumb for common cases and do not apply in all circumstances. For instance, many Cocoa objects have a delegate outlet. To connect these, you draw a connection line from the Cocoa object to your custom object.
Another way to clarify connections is to consider who needs to find whom. With outlets, the custom object needs to find some other object, so the connection is from the custom object to the other object. With actions, the control object needs to find the custom object, so the connection is from the control object to the custom object.
ConverterController needs to communicate with the user-interface elements in the Currency Converter window. It must also communicate with an instance of the Converter class, defined in “Defining the Converter Class.” The Converter class implements the conversion computation.
To add the outlets required by the ConverterController class:
Select ConverterController in the Classes pane in the MainMenu.nib window.
Choose Add Outlet to ConverterController from the Classes menu, or:
Choose Attributes from the inspector pop-up menu.
Click 0 Outlets.
Click Add.
Name this outlet rateField and press Return.
Since the rateField outlet is still selected, all you have to do to create more outlets is press Return. Do this once to create the dollarField outlet, and again for the amountField outlet.
Add another outlet named “converter”. This is the outlet ConverterController will use to communicate with the Converter instance. (The Converter class is defined later in this chapter.)
Notice the Type column in the table of outlets. By default, the type of outlets is set to id. It rarely works to leave it as id—Java is a statically typed language, and the Java compiler will not compile your program if it invoked any methods implemented by Cocoa classes on an object declared as id. You may sometimes get lucky—for example, if you do not call any of the object’s intended class methods—but it’s a good idea to get into the habit of setting the types for outlets. (This practice is not required when using Objective-C but improves the application’s performance.) Change the type of the three outlets to NSTextField by choosing it from the pop-up menus currently set to id.
The result of these operations is shown in Figure 1-19.
ConverterController has one action method, convert. When the user clicks the Convert button, the convert method is invoked on the target object, an instance of ConverterController. “Action” refers both to a message sent to an object when the user clicks a button or manipulates some other control object and to the method that is invoked. To add the convert method to ConverterController:
Select ConverterController in the Classes pane in the MainMenu.nib window.
Choose Add Action to ConverterController from the Classes menu, or:
Choose Attributes from the inspector pop-up menu.
Click 0 Actions.
Click Add.
Type convert() in the Action Name list and press Return.
Last updated: 2006-10-03