- In Xcode
- In Interface Builder
- Back in Xcode
- Documentation
- What Have You Done?
Back in Xcode
In Xcode, you will see that Foo.h and Foo.m have been added to the project. Most programmers would put these files under the Classes group (Figure 2.27). Drag the files into the Classes group if they aren't there already.
Figure 2.27 The New Class in Xcode
If this is the first time that you are seeing Objective-C code, you may be alarmed to discover that it looks quite different from C++ or Java code. The syntax may be different, but the underlying concepts are the same. For example, in Java a class would be declared like this:
import com.megacorp.Bar; import com.megacorp.Baz; public class Rex extends Bar implements Baz { ...methods and instance variables... }
This says, "The class Rex inherits from the class Bar and implements the methods declared in the Baz interface."
The analogous class in Objective-C would be declared like this:
#import <megacorp/Bar.h> #import <megacorp/Baz.h> @interface Rex : Bar <Baz> { ...instance variables... } ...methods... @end
If you know Java, Objective-C really isn't so strange. Note that like Java, Objective-C allows only single inheritance; that is, a class has only one superclass.
Types and Constants in Objective-C
Objective-C programmers use a few types that are not found in the rest of the C world.
-
id is a pointer to any type of object.
-
BOOL is the same as char, but is used as a Boolean value.
YES is 1.
NO is 0.
-
IBOutlet is a macro that evaluates to nothing. Ignore it. (IBOutlet is a hint to Interface Builder when it reads the declaration of a class from a .h file.)
-
IBAction is the same as void. It also acts as a hint to Interface Builder.
-
nil is the same as NULL. We use nil instead of NULL for pointers to objects.
Look at the Header File
Click on Foo.h. Study it for a moment. You should see that it declares Foo to be a subclass of NSObject. Instance variables are declared inside the curly braces.
#import <Cocoa/Cocoa.h> @interface Foo : NSObject { IBOutlet NSTextField *textField; } - (IBAction)generate:(id)sender; - (IBAction)seed:(id)sender; @end
#import is similar to the C preprocessor's #include. However, #import ensures that the file is included only once.
Notice that the declaration of the class starts with @interface. The @ symbol is not used in the C programming language. To minimize conflicts between C code and Objective-C code, Objective-C keywords are prefixed by @. Here are a few other Objective-C keywords: @end, @implementation, @class, @selector, and @encode.
In general, you will find entering code easier if you turn on syntax-aware indention. In Xcode's Preferences, select the Indentation pane. Check the box labeled Syntax-aware indenting, as shown in Figure 2.28.
Figure 2.28 The New Class in Xcode
Edit the Implementation File
Now look at Foo.m. It contains the implementations of the methods. In C++ or Java, you might implement a method something like this:
public void increment(Object sender) { count++; textField.setIntValue(count); }
In English, you would say, "increment is a public instance method that takes one argument that is an object. The method doesn't return anything. The method increments the count instance variable and then sends the message setIntValue() to the textField object with count as an argument."
In Objective-C, the analogous method would look like this:
- (void)increment:(id)sender { count++; [textField setIntValue:count]; }
Objective-C is a very simple language. It has no visibility specifiers: All methods are public, and all instance variables are protected. (Actually, there are visibility specifiers for instance variables, but they are rarely used. The default is protected, and that works nicely.)
In Chapter 3, we will explore Objective-C in all its beauty. For now, just copy the methods:
#import "Foo.h" @implementation Foo - (IBAction)generate:(id)sender { // Generate a number between 1 and 100 inclusive int generated; generated = (random() % 100) + 1; // Ask the text field to change what it is displaying [textField setIntValue:generated]; } - (IBAction)seed:(id)sender { // Seed the random number generator with the time srandom(time(NULL)); [textField setStringValue:@"Generator seeded"]; } @end
(Remember that IBAction is the same as void. Neither method returns anything.)
Because Objective-C is C with a few extensions, you can call functions (such as random() and srandom()) from the standard C and Unix libraries.
Build and Run
Your application is now finished. To build and run the application, click on the hammer/green circle toolbar item (Figure 2.29). If your app is already running, the toolbar item will be disabled; quit your app before trying to run it again.
If your code has an error, the compiler's message indicating a problem will appear at the view in the upper-right corner. If you click on the message, the erroneous line of code will be selected in the view on the lower right. In Figure 2.29, the programmer has forgotten a semicolon.
Launch your application. Click the buttons and see the generated random numbers. Congratulationsyou have a working Cocoa application.
awakeFromNib
Notice that your application is flawed: When the application first starts, instead of anything interesting, the words System Font Text appear in the text field. Let's fix that problem. You will make the text field display the time and date that the application started.
The nib file is a collection of objects that have been archived. When the program is launched, the objects are brought back to life before the application handles any events from the user. Notice that this mechanism is a bit unusualmost GUI builders generate source code that lays out the user interface. Instead, Interface Builder allows the developer to edit the state of the objects in the interface and save that state to a file.
After being brought to life but before any events are handled, all objects are automatically sent the message awakeFromNib. You will add an awakeFromNib method that will initialize the text field's value.
Add the awakeFromNib method to Foo.m. For now, just type it in. You will understand it later on. Briefly, you are creating an instance of NSCalendarDate that represents the current time. Then you are telling the text field to set its value to the new calendar date object:
- (void)awakeFromNib { NSCalendarDate *now; now = [NSCalendarDate calendarDate]; [textField setObjectValue:now]; }
The order in which the methods appear in the file is not important. Just make sure that you add them after @implementation and before @end.
You will never have to call awakeFromNib; it gets called automatically. Simply build and run your application again. You should now see the date and time when the app runs (Figure 2.30).
Figure 2.30 Completed
In Cocoa, a lot of things (like awakeFromNib) get called automatically. Some of the confusion that you may experience as you read this book will come from trying to figure out which methods you have to call and which will get called for you automatically. I'll try to make the distinction clear.