This article is more than 1 year old

Making the move to the Mac

Part Two

Last time round, we cautiously dipped a toe into the Mac development waters from the perspective of a Windows programmer with RAD experience. Along the way, I briefly looked at the rudiments of Objective-C syntax, and I described terms such as 'framework' and 'Cocoa'. (Remember, the idea here is not to turn you into a killer Mac coder, but rather to look at the essentials of mainstream Mac development using terminology that's familiar to a .NET, Delphi, C++ or VB developer.)

With this as a basis, let's turn our attention to Xcode and Interface Builder. These are the two main development tools used by a Cocoa developer. (As I said last time, it's perfectly possible to create Cocoa apps using only command-line tools. It's not nice, but if you're that way inclined...)

Xcode

From the viewpoint of a Windows developer, Xcode might seem somewhat lacking. Selecting the File | New Project option gives an impressive looking set of choices. Choose 'Cocoa Application' for a plain vanilla Cocoa app, and you end up looking at a list of boilerplate files which have been automatically added to your fledgling project. But where lurks the form designer? Whither yon component palette?

The answer is that Xcode handles the Objective-C side of things while its partner in crime, Interface Builder, takes care of your GUI window layout. Coming from Delphi, or VB, this seems cumbersome. Truth to tell, it is cumbersome, but I guess you don't miss what you don't know. To fire up Interface Builder (IB for short) look for the file called MainMenu.nib and double-click it.

Historically, the 'nib' suffix stands for 'NeXTSTEP Interface Builder', the great-grandfather of the current IB. In Delphi terms, a nib file is somewhat like a DFM; it encapsulates not only the size and location of every control in a window, but also all the property settings of each control. Finally, it also describes how different objects are linked together via actions and outlets. Actions and outlets? For now, think of this as being similar to event handlers in Delphi or .NET. I'll talk more about actions and outlets when discussing IB, below.

Note: Admittedly, this is something of a generalisation. A nib file doesn't actually have to relate to a GUI element such as a window at all. A nib might contain something like an instance of a non-visual application controller class which is automatically instantiated when your program starts running. Again, this will become clear later.

The Xcode environment has an integrated code editor which I rather like, although it's possible to work with an external editor if you prefer. (Popular alternatives include BBEdit and TextMate.) It also takes care of obvious things like building and running your project, adding new source files, and so forth. Xcode makes it easy to add new frameworks to a project - just drag the wanted framework from /System/Library/Frameworks (or wherever it happens to be) and drop it into the Groups and Files pane of the project window.

Xcode is somewhat let down by the debugger, which will seem relatively crude to anyone who's used to the superb debugging facilities built into Microsoft's .NET development tools. To some extent, Apple are constrained by the need to wrap the IDE's debugging facilities around gdb, the underlying GNU debugger which does all the heavy lifting. If you can't achieve what you want with the visual interface, it's possible to work with gdb directly during a debugging session.

Interface Builder

As mentioned above, IB takes care of GUI layout and is also used to link together different objects. Suppose you want to create a controller class for your application. Within IB, you might subclass the base NSObject class to create a new class called AppController. You would typically instantiate AppController, also within IB. Finally, you would create the source and header files for your new controller class - IB will automatically add them to the current Xcode project.

Back in Xcode, you'll find that AppController.m and AppController.h have been added to your project. But at this point, they don't do much! Try adding the following code to the AppController.m file:

- (void) awakeFromNib
{
  NSLog (@"Hello from AppController...");
}

When you run the app, the expected message will appear in the IDE's Run Log window. So what's happening here? The key point is that when a Cocoa application starts executing, the MainMenu.nib file is automatically loaded into memory and any objects found within the nib are instantiated; you don't have to write the code to do this manually. When all objects in the nib have been un-archived, and after any inter-object connections have been set up, the Cocoa framework sends an awakeFromNib message to each freshly instantiated object, provided that it responds to that message.

The bottom line is that - using IB alone - you can build quite a complex hierarchy of visual and non-visual objects that constitute the backbone of your application&'s structure and visual appearance. You then use Xcode to put flesh on the bones, so to speak.

Earlier, I mentioned actions and outlets. This is the primary mechanism for interconnecting objects. As it stands, our hypothetical AppController object is deaf and dumb - we need to connect it up to the outside world. Back in IB, you might drag a push-button onto the default window. You'll obviously want something to happen when the button is pushed; it makes sense to send a message to our AppController. Firstly, you'll need to define an 'action' for the controller object. Again, this is all done in IB. Once defined, you then (here's the initially counter-intuitive bit!) control-drag the mouse from the push-button to the instance of AppController. This tells IB that we want AppController to do something in response to the button click. IB will present a list of available actions on the controller object. Select one, click the Connect button and you're done.

Back in Xcode, you can then write the code for the action method. It will typically have a signature like this:

- (IBAction) doit: (id) sender
{
  NSLog (@"Oh - that really pressed my button!");
}

As you will appreciate, an action effectively maps from the GUI to program code; whenever the user does something with the user interface, we need to call an action. Contrariwise, an outlet goes the other way, mapping from the code to a GUI element. As an example, consider our push-button control again. Under certain circumstances, we might want to disable the button if it's not appropriate to press it. To do that, the code needs to 'see' the button. An outlet is essentially a pointer to the control. Like actions, outlets are set up when the nib is loaded and processed, before awakeFromNib is called.

Next time, I'll bring the whole thing together by walking you through the development of a small, self-contained application. I also hope to mention Leopard (Apple's soon to be released operating system) with special emphasis on the changes and improvements to both the development tools and to Objective-C itself. That's assuming it's been released by this time next month. If not, my lips will have to remain sealed!

In the meantime, here's the URL of a small pdf file which gives a good introduction to the Objective-C language. Definitely worth a read. ®

More about

TIP US OFF

Send us news


Other stories you might like