The iPhone/iPod Touch SDK is gradually taking its way into main stream development for high-end mobile devices. Waiting to see what the Open Handset Alliance is going to deliver within the next year thanks to the Android platform, I felt necessary and interesting diving into Apple's SDK.
Once it was published some weeks ago I found out it was entirely Objective-C based, thus I decided to start taking a look at the language. This is my first post about my own learning experience of Objective-C 2.0.
Interfaces
Every object has to expose an interface that defines the contract it must adhere to and its internal structure (the set of properties it holds). This is deeply different from what an interface is in Java i.e.
Let's look at an example:
// Person.h
// Objective-C test 1
//
// Created by Francesco Russo
// Copyright 2007. All rights reserved.
//
#import <Cocoa/Cocoa.h>
// Interface Person inherits from NSObject...
@interface Person : NSObject {
// ... and has the following private properties
@private
NSDate *birthday;
NSString *name;
NSString *surname;
NSSet *friends;
}
// Along the properties above each Person must expose the following set of methods
// NOTE: @property annotation tells the compiler that the following
@property(copy) NSDate *birthday;
@property(copy) NSString *name;
@property(copy) NSString *surname;
@property(copy) NSSet *friends;
// instance methods are marked with a minus sign "-", while class methods are marked
// with a "+" (plus) sign
- (void)reluctantMessage;
@end
The snippet above defines a Person interface that encloses four private properties (see the @private annotation) and the implicit definition of their accessor/mutator messages. This is accomplished by means of the @property annotation.
One of the things a like most is the copy attribute of the @property annotation: it tells the Objective-C (compiler?) to generate accessor messages that must return a copy of the given property! That's really cool, it would be great if Java had something similar. We also have similar options to declare synchronized messages.
Well, once we are done with our properties we can deal with messages: messages are functions that can be defined on a per-class or per-instance basis (just like static and instance methods in Java).
Implementations
Once our interface has been completed it's time to give it an implementation:
// Person.m
// Objective-C test 1
// Copyright 2007. All rights reserved.
//
#import "Person.h"
// implementation of the Person interface
@implementation Person
// the @synthesize annotation allows for automatic accessor&mutator methods
// generation at compile time
@synthesize name;
@synthesize surname;
@synthesize birthday;
@synthesize friends;
// implementation of the instance method defined in the interface
- (NSString*)description {
NSString *str = nil;
@try {
// result NSString initialization
str = [[NSString alloc] initWithString:@""];
// result NSString composition
str = [[[[[[str stringByAppendingString:name]
stringByAppendingString:@" "]
stringByAppendingString:surname]
stringByAppendingString:@".\nBirthday is: "]
stringByAppendingString:[birthday description]]
stringByAppendingString:@".\nHas friends: "];
// temp variable required for iterating over the
// set of friends
// be careful: if two Persons are friends to eachother,
// invoking "description" in the following iteration will
// result in an infinite loop
Person *friend;
for (friend in friends) {
str = [str stringByAppendingString:[friend name]];
str = [str stringByAppendingString:@"\n"];
}
} @catch (NSException *e) {
NSLog(@"description: Caught %@: %@", [e name], [e reason]);
} @finally {
return str;
}
}
// This is a 'reluctant' method, that is a method that won't work at all ;)
-(void) reluctantMessage {
// a try block
@try {
// throwing an exception
@throw [NSException exceptionWithName:@"My first exception" reason:@"Only playing around :P" userInfo:nil];
}
@catch (NSException * e) {
// that gets caught and logged
NSLog(@"Exception description: caught %@: %@", [e name], [e reason]);
}
@finally {
// and a finally block that always executes
NSLog(@"This is a 'finally' block.");
}
}
@end
This class states it implements the Person interface defined above.
The first thing we can highlight is that again we can avoid writing boilerplating code for our accessor&mutator messages by means of the @synthesize annotation.
As a second step I've overridden an NSObject message called description that can be thought of as the Java's Object toString() method. This message simply provides an NSString-based representation of the Person instance.
Interesting things to note:
- the way exceptions are handled by means of the keywords @try, @catch, @finally
- the foreach iteration
- interface methods can only be public
A main functionNow let's @finally :P take a look @ the main that tests the few lines of code written above:
// main.m
// Objective-C test 1
//
// Copyright 2007. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import <Person.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
// every time a new object has to be created, we need to invoke
// the NSObject's static "alloc" method, followed by some initialization
// method
Person *person_1 = [[Person alloc] init];
// "inline" NString instances must start with @ (the address of)
person_1.name = @"Foo";
person_1.surname = @"Bar";
NSDate *birthday = [NSDate dateWithNaturalLanguageString:@"01/01/2008"];
person_1.birthday = birthday;
Person *person_2 = [[Person alloc] init];
person_2.name = @"Alice";
person_2.surname = @"Bob";
person_2.birthday = [NSDate dateWithNaturalLanguageString:@"01/01/2008"];
person_1.friends = [[[NSSet alloc] init] setByAddingObject:person_2];
person_2.friends = [[[NSSet alloc] init] setByAddingObject:person_1];
NSLog(@"\nPerson is: %@", [person_1 description]);
NSLog(@"\nPerson is: %@", [person_2 description]);
[person_1 reluctantMessage];
[person_2 reluctantMessage];
return 0;
}
Quite nice, isn't it? Hope I'll find the time to take my studies further at the point required for writing some simple iPhone/iPod Touch application.
Cheers,
F.