Páginas

Sunday, August 29, 2010

An Objective-C/Cocoa character counter program

I've been learning Objective-C and Cocoa since yesterday (yes, I'm a newbie, so don't judge... :P), but I'm really loving it.

For those who have no idea of what this is, Objective-C is a C object oriented extension and Cocoa is, according to the Wikipedia, one of Apple Inc.'s native object-oriented API's for the Mac OS X operating system. Both of them together provide a great way to create programs to Mac OS X.

I'm not going to try to explain Objective-C or Cocoa in detail, there are really good books for that (the idea for this example was taken from Cocoa® Programming for Mac® OS X (3rd Edition)), nor will I explain how to use Xcode as an IDE. I'll just write the code and a little explanation of what it does.

So, let's get to what really matters, the code! :D

Every class in Obj-C is composed by two files a header file (.h) and a source file (.m). The first one has the instance variables and methods declarations and the second one the actual code (remember that Objective-C is an extension of C).

Our header file will be something like this:

#import <Cocoa/Cocoa.h>

@interface Counter : NSObject {
    IBOutlet NSTextField *line;
    IBOutlet NSTextField *output;
}

-(IBAction)count:(id)sender;
@end


The first line imports the declaration of NSObject which Counter inherits from, similar to Java's Object. All the Objective-C keywords start with @, to minimize conflicts with C code, as @interface.

Both instance variables are of type pointer to NSTextField, that can be either a text field or a label. IBOutlet is a macro that evaluates to nothing, it's a hint to the Interface Builder.

Finally, there's the method declaration. The method has the name count, returns IBAction (the same as void, also a hint for the IB) and has one argument named sender and of type id (a pointer to any type of object).

The .m file will have to implement the declared method (Java's public methods), but other methods can be declared as well, new methods (Java's private methods) or override inherited methods. Ours will be like this:


#import "Counter.h"

@implementation Counter

-(void)awakeFromNib
{
    [output setStringValue:@"???"];
}
   
-(IBAction)count:(id)sender
{
    NSString *theLine = [line stringValue];
    int noChars = [theLine length];
    NSLog(@"Counted %d chars",noChars);
    [output setStringValue:[NSString stringWithFormat:@"'%@' has %d characters", theLine, noChars]];
}   

@end


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. After being brought to life but before any events are handled, all objects are automatically sent the message awakeFromNib. This means that the label will have the string value "???" when the program starts, it should look like the following picture:


In the Interface Builder you should connect the text field and label to the corresponding outlets and the action of the button to the count method. For this you will have to add an object of the class Counter to the Doc Window.


Now, you've made sure the count method will be called when the button is pressed. So, what does the count method do?

It's actually a pretty simple method, it gets the string the user has typed, by sending the stringValue message to the text field (this method is an accessor, the correspondent to Java's get). It then sends the length message to the string, to get the number of characters. We now have all the information we need.

After that it writes the character number to the console, and finally it sets the label's string.

The final result should be something like this:



In this code I assume that you have a version of Mac OS above 10.4 and that you have the garbage collector on. I'll cover the usage of retain counts, the alternative solution to the GC in the future.

No comments: