Journal Articles
Browse in : |
All
> Journals
> CVu
> 166
(12)
All > Topics > Programming (877) Any of these categories - All of these categories |
Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.
Title: An Introduction to Objective-C
Author: Administrator
Date: 03 December 2004 13:16:08 +00:00 or Fri, 03 December 2004 13:16:08 +00:00
Summary:
Part 3 - An Example Using Foundation
Body:
The best way to get the feel of a programming language is to have a look at actual code. This demonstration program I've written consists of three Objective-C source files:
Listing 1 shows main.m, which reads file names from the command line and prints the unique lexical tokens found in each file. Tokens are strings of printable characters separated by whitespace and punctuation marks.
Listing 2 shows StringTokenizer.h, which declares the public interface of the class StringTokenizer. Private methods, being part of the implementation, typically have no place in this file.
Listing 3 shows StringTokenizer.m, which contains the implementation of the StringTokenizer class.
Listing 1: main.m
#import <Foundation/Foundation.h> #import "StringTokenizer.h" // Prints unique tokens found in files // supplied as arguments. int main(int argc, const char *argv[]) { // Create a pool of items to be garbage-collected NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; if(argc > 1) { int i; for(i = 1; i < argc; ++i) { // Read contents of file into string NSString *path = [NSString stringWithCString:argv[i]]; NSString *myString = [NSString stringWithContentsOfFile:path]; if(myString == nil) { fprintf(stderr, "File %s not found\n", [path cString]); // The system will clean up anyway when we // exit, but we do this for form's sake [pool release]; return 1; } // Create our tokenizer StringTokenizer *tokenizer = [[[StringTokenizer alloc] initWithString:myString andDelimiters:@" ,.!?;:\t\r\n"] autorelease]; // Create a set with room for 100 items to // hold unique tokens NSMutableSet *theSet = [NSMutableSet setWithCapacity:100]; // Get the first token NSString *token = [tokenizer nextToken]; while(token != nil) { // This will fail if there is an identical // token there already [theSet addObject:token]; // Get more tokens token = [tokenizer nextToken]; } // Print out unique tokens in the set in case- // insensitive alphabetical order NSArray *tokens = [[theSet allObjects] sortedArrayUsingSelector:@selector( caseInsensitiveCompare:)]; printf("Unique tokens in %s:\n", [path cString]); int j; for(j = 0; j < [theSet count]; ++j) printf("\t%d %s\n", j+1, [[tokens objectAtIndex:j] cString]); } } else fprintf(stderr, "Usage: StringTokenizer file1 file2 ...\n"); // Trigger autorelease of allocated memory [pool release]; return 0; }
Listing 2: StringTokenizer.h
// Minimal tokenizer class, useful for // demonstration purposes only #import <Foundation/Foundation.h> @interface StringTokenizer : NSObject { NSString *data; NSCharacterSet *delimiters; size_t position, dataSize; } // Default initializer - object contains no data // and delimiters string set to space, tab, newline // and return - (id)init; // Initialise object with data string; // delimiters string is set to space, tab // newline and return. - (id)initWithString:(const NSString *)aString; // Initialise object with data string to tokenise // and a set of delimiters to ignore - (id)initWithString:(const NSString *)aString andDelimiters:(NSString *)delims; // Assign the object a new data string to // tokenise - (void)setData:(const NSString *)aString; // Assign the object a new set of delimiters to work // with - (void)setDelimiters:(NSString *)delims; // Return the next token from the data string or nil // if none exists - (NSString *)nextToken; @end
Listing 3: StringTokenizer.m
#import "StringTokenizer.h" // Default delimiters are whitespace #define DEFAULT_DELIMITERS @" \t\n\r" // Create a category to forward-declare // private method in order to avoid // compiler warnings about undeclared methods. @interface StringTokenizer (Private) - (void)skipDelimiters; @end @implementation StringTokenizer - (id)init { return [self initWithString:nil andDelimiters:DEFAULT_DELIMITERS]; } - (id)initWithString:(const NSString *)aString { return [self initWithString:aString andDelimiters:DEFAULT_DELIMITERS]; } // This is the designated initialiser, which // is called by all the other initialisers and // does all the work - (id)initWithString:(const NSString *)aString andDelimiters:(NSString *)delims { if(self = [super init]) { position = 0; data = [aString retain]; // Cache length of data string dataSize = [data length]; delimiters = [[NSCharacterSet characterSetWithCharactersInString:delims] retain]; } return self; } - (void)setData:(const NSString *)aString { if(aString != data) { [data release]; position = 0; // We are starting from scratch data = [aString retain]; dataSize = [data length]; } } - (void)setDelimiters:(NSString *)delims { [delimiters release]; delimiters = [[NSCharacterSet characterSetWithCharactersInString:delims] retain]; } - (NSString *)nextToken { if(data == nil || position >= dataSize) return nil; [self skipDelimiters]; if(position >= dataSize) return nil; size_t oldPosition = position; // Save current position BOOL nonDelim = YES; // Assume that the next // character is a non-delimiter while(position < dataSize && nonDelim) { // Test for a match in the delimiter string of // the character at the current position in the // data string; if no match is found, increment // position and proceed. if(![delimiters characterIsMember: [data characterAtIndex:position]]) position++; else nonDelim = NO; } // Create a string containing the token and return // it. Type NSRange is a struct containing two // members: location and length NSRange range = {oldPosition, position-oldPosition}; return [data substringWithRange:range]; } - (void)skipDelimiters { BOOL nonDelim = NO; // Non-delimiter character not yet found while (position < dataSize && !nonDelim) { // Test for a match in the delimter string of // the character at the current position in the // data string; if a match is found, increment // position and proceed. if([delimiters characterIsMember: [data characterAtIndex:position]]) position++; else nonDelim = YES; } } // Invoked automatically when object is released - (void)dealloc { // Release memory allocated for our instance // variables [data release]; [delimiters release]; [super dealloc]; } @end
The preprocessor directive #import is generally used in Objective-C; it differs from #include in that it ensures that a header file is included only once even if it does not contain guard macros.
Foundation's memory management involves semi-automated reference counting. Pointers to all the objects allocated in the main function will be added to the autorelease pool, and when this pool is released, the method -dealloc is called on all the objects before the memory they occupy is freed.
Memory allocation for objects is usually provided by the method +alloc in the root class, NSObject. -retain increases the reference count by one, while -release decrements it. -autorelease adds the receiver to the autorelease pool.
NSString is Foundation's basic string-handling class. NSString objects hold Unicode strings that cannot be changed once created; objects of its subclass NSMutableString allow their contents to be edited.
StringTokenizer is a class I have written to extract tokens from a string. It has limited functionality but is sufficient for the purposes of this demo. Tokens are extracted by calling nextToken repeatedly until nil is returned. An instance of StringTokenizer is created by calling the class method alloc to allocate storage for the object and then the object is initialised by the instance method initWithString:andDelimiter:, which does the same kind of work as a constructor in C++ and Java.
The Objective-C keyword nil refers to a null object. It differs from the macro NULL in that it is perfectly legal and safe to send messages to nil.
NSMutableSet is a subclass of NSSet, from which it differs by allowing objects to be inserted and deleted after it has been initialised. Instances of NSSet and NSMutableSet are unordered collections of values, where each value occurs at most once.
Objects of class NSArray are immutable ordered collections of objects. The line:
NSArray *tokens = [[theSet allObjects] sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
deserves some comment. First the message allObjects sent to the set causes it to return an NSArray of its contents in arbitrary order; this NSArray object then receives the message sortedArrayUsingSelector: with the selector of NSString's caseInsensitiveCompare: method as argument. (The compiler directive @selector turns a method name into a selector.) The NSArray object's method sortedArrayUsingSelector then returns a new copy of itself with the NSString objects in case-insenstitive ascending collating order.
The StringTokenizer class contains an instance variable called delimiters of type NSCharacterSet. Its method, characterIsMember: is called for each character of the string in turn; if the character is found in the delimiters character set, it is skipped, and any non-delimiter or unbroken sequence thereof is recognised as a token and returned.
Notes:
More fields may be available via dynamicdata ..