Chapter 2. Foundation

The Foundation framework provides support for a variety of basic functionalities and data types, including the following:

This chapter discusses these subjects and provides several short examples that demonstrate of the most common methods of the key classes.

The Foundation framework provides many classes and protocols that extend the capabilities of the Objective-C language to represent and work with basic data types, such as strings and numbers, in an object-oriented fashion. Additionally, the Foundation framework provides application programming interfaces (APIs) for working with more complex data types, such as dates and collections.

Two of the most basic data types in an application are strings and numbers . The Foundation framework provides object abstractions in the form of NSString and NSNumber, and an extensive API to manipulate them.

Foundation’s primary class used to represent and manipulate strings is NSString. Instances of NSString can be considered, at their core, an immutable array of Unicode characters, and can represent characters from the alphabets of nearly every written language, past and present. In fact, NSString is a class cluster, which shields the developer from a number of underlying implementation details that make string handling more efficient. This abstraction is generally relevant only when subclassing NSString, so it will not be considered further here.

Objective-C provides a syntax shortcut to create strings in code that is of the form @"...“. In code, this looks like:

NSString *str = @"Hello";

When interpreted by the compiler, this syntax translates into an NSString object that is initialized with the 7-bit ASCII encoded string between the quotes. This string object is created at compile-time and exists for the life of the application. While you may send retain and release messages to an NSString object created from the literal syntax, such an object will never be deallocated. Example 2-1 shows several NSString methods. For more information on using printf -style formatting, see /Developer/Documentation/Cocoa/TasksAndConcepts/ProgrammingTopics/DataFormatting/iFormatStrings.html.

Example 2-1. Creating instances of, and working with, NSString

                        // The literal syntax for an NSString object
NSString *str = @"Hello";

// Create one string from another string
NSString *str2 = [NSString stringWithString:str];

// You can also create a string using printf style formatting
str = [NSString stringWithFormat:@"%d potatoes", 10];

// The contents of a text file may be used to initialize a string
str = [NSString stringWithContentsOfFile:@"/path/to/file"];

// C character arrays may be used to create a string as well
char *cStr = "Hello again";
str = [NSString stringWithCString:cStr]; 

// How to get a C string from an NSString
char cStr = [str UTFString];

// Determine the length of a string, which is a count of the
                        // number of Unicode characters in the string
unsigned int strLength = [str length];

// Append one NSString to another
                        // str2 = "Hello, World!"
str2 = [str stringByAppendingString:@", World!"];

// Append a format to an NSString
                        // str3 = "Hello, World! 2003"
NSString *str3 = [str2 stringByAppendingFormat:@" %d", 2003];

// Extract substrings; returns characters 6 to the end
                        // subStr = @"World! 2003"
NSString *subStr = [str3 substringFromIndex7];

// Returns characters from beginning to character 5
                        // subStr = @"Hello"
subStr = [str3 substringToIndex:5];

// Returns 6 characters starting at index 7; 
                        // Also see the comment that accompanies NSRange
                        // subStr = @"World!"
subStr = [str3 substringWithRange:NSMakeRange(7, 6)];

// Case conversion; returns capitalization: "Hello, World"
NSString *firstcaps = [str2 capitalizedString];

// Case conversion; returns lowercase: "hello, world!"
NSString *lower = [str2 lowercaseString];

// Case conversion; returns uppercase: "HELLO, WORLD!"
NSString *upper = [str2 uppercaseString];

// Searching for substrings; returns NSRange {0, 2}
NSRange loc = [str2 rangeOfString:@"He"];

// Searching for substrings; returns NSRange {NSNotFound, 0}
loc = [str2 rangeOfString:@"and"];

// Checking whether a string is a prefix or suffix of another
BOOL r = [str2 hasPrefix:@"Hello, W"];   // Returns YES
BOOL r = [str2 hasSuffix:@"What?"];      // Returns NO

To initialize an NSString from Unicode characters, first assemble a C array of the Unicode character codes, which are of the type unichar. Example 2-2 shows how to use hexadecimal character codes to specify the Unicode characters for the string "α β γ δ “:

Tip

The entire Unicode character set catalog is available at http://www.unicode.org. This site offers a way for you to find the hexadecimal code for any character. In addition, Mac OS X also provides the Character Palette utility, found in the Input Menu, which can be used to look up character codes of any Unicode character. In Example 2-2, the Unicode characters were specified by their hexadecimal code because the default text encoding of source files (Mac OS Roman or another 8-bit encoding) doesn’t allow direct representation of Unicode characters. However, Project Builder lets you specify Unicode (UTF-16 and UTF-8) as the text encoding for a source file, which would let you enter the Unicode characters directly into source strings with the Character Palette. The file encoding is specified globally in the Project Builder preferences, or on a per-file basis in the file’s Project Builder info panel.

NSString includes a method used to break a string apart into components, based on a given separator character or string. This might be useful if you need to parse a record line from a text file whose fields are delimited by a character or string. Example 2-3 shows how this works for a string with colon-separated fields.

NSMutableString extends NSString’s functionality to support in-place modification. This additional flexibility is provided at the expense of decreased efficiency. Example 2-4 illustrates several commonly used methods in NSMutableString.

NSString provides several methods for comparing strings and testing equality. NSObject declares the method isEqual: to test general object equality. This method works with NSString objects, but the NSString method isEqualToString: more efficiently tests the equality of two objects known to be strings. Using it returns YES if the ids of the two strings are equal (which implies that the variables point to the same object) or if the result of a lexical comparison between the strings is NSOrderedSame.

A comparison that determines the lexical ordering of two strings is carried out with any of several methods, each of which provides varying degrees of control over the scope of the comparison. The method that provides the greatest amount of control is compare:options:range:. The options: argument takes one or both of the following two constants (both can be used with the C bitwise OR operator, |):

The range: argument restricts the comparison to a substring of the receiver. If you want to compare only the first two string characters, specify an NSRange of (0,2) in the range: argument.

Two other related methods are compare:options: and compare:. The first method passes options: to compare:options:range: and makes the range equal to the entire length of the receiver. The second, compare:, passes no options, and again uses the full extent of the receiver as the range. Example 2-5 shows different ways to compare strings.

An NSScanner object lets you search an NSString object for string and number values, with options for scanning up to or past characters from a given set or string. You would usually initialize a scanner with the string to scan, using scannerWithString: or initWithString. You can configure it to be case-sensitive, or not, with setCaseSensitive; establish a starting point with setScanLocation:; or set its locale with setLocale:. A scanner’s locale affects the way it interprets values from the string. In particular, a scanner uses the locale’s decimal separator to distinguish the integer and fractional parts of floating-point representations.

After it is configured, a scanner can read numeric values from its string into a variable, using methods such as scanInt:, scanFloat:, and scanDecimal: (the first two methods read scalars; scanDecimal: creates an NSDecimalNumber object). You can search for particular strings or characters by using any of the following methods:

All of these methods return a Boolean value to indicate the operation’s success. Pass a pointer to the variable as the argument to these methods, or pass nil to skip a value. Finally, check whether you have reached the end of the input string with the isAtEnd method. For example, assume a file, ~/scannerTest.txt, of the form:

EmpId: 7830480 FirstName: Jo LastName: Wong
EmpId: 67567456 FirstName: Toni LastName: Jones
EmpId: 546776 FirstName: Dylan LastName: Blimp

Example 2-6 shows how the file may be parsed with NSScanner.

The code in Example 2-6 produces the following output:

Jo Wong, EmpID: 7830480
Toni Jones, EmpID: 67567456
Dylan Blimp, EmpID: 546776

For many numerical operations dealing with calculations, using C’s primitive numerical data types is the easiest and most efficient way to represent numerical data. However, you might need to treat a number as an object to store it in a collection or to store a number in the user defaults database. For such situations, the Foundation framework provides the class NSNumber , which is an Objective-C wrapper class for the standard numeric data types in C. You can initialize instances of NSNumber with a scalar and retrieve a scalar value from a number object. Example 2-7 shows many of the methods used to work with NSNumbers.

NSDecimalNumber extends the capabilities of NSNumber with APIs to perform base-10 arithmetic, and it provides methods to initialize an instance in terms of the number’s basic components. Example 2-8 shows how to work with NSDecimalNumber.

Each method has corresponding methods that let you determine how to handle rounding and errors (typically by using instances of NSDecimalNumberHandler), which is known as the number’s behavior. Numbers round to the nearest integer by default; an exception is raised if there is division by zero, or if the result of a calculation exceeds the maximum or minimum numbers that can be represented.

The Foundation framework offers several important classes for creating and manipulating collections of objects. The primary collection classes are NSArray, NSDictionary, and NSSet:

Each class has subclasses that extend their interfaces to provide mutability.

Instances of NSArray represent an ordered collection objects. An index number identifies each member object in the array; indexing begins at zero, just as in C arrays. Example 2-9 gives an overview of NSArray’s capabilities.

NSMutableArray extends NSArray by adding support for arrays whose contents can be changed after their initialization. Example 2-10 shows how a small set of the mutability methods works.

NSSet declares an interface to unordered collections of unique objects. The Foundation framework implements two subclasses of NSSet: NSMutableSet and NSSCountedSet, which is a child class of NSMutableSet. Like arrays and dictionaries, the contents of a set can be any Objective-C object. Example 2-11 shows how to use NSSet.

Example 2-12 shows what NSMutableSet adds to NSSet.

NSCountedSet is based on a slightly different idea of a set than its superclasses. In a standard set, each member object must be unique. Counted sets remove the constraint on uniqueness, making it possible to add an object to a counted set more than once. However, a counted set does not keep multiple references to an object; rather, it keeps a count of the number of times an object was added to the set. Whenever an object is added to the set of which it is already a member, the count for that object is incremented. When an object is removed from a counted set, its count is decremented until it reaches zero, at which point the object is no longer a member of the set. The code in Example 2-13 demonstrates the functionality added by NSCountedSet.

Cocoa dictionaries provide a collection class that implements the idea of key-value pairs. In a dictionary, member objects are associated with a unique identifier key, used to identify and access the object. Although keys are typically NSString objects, both keys and values may be of any class. Example 2-14 summarizes several commonly used methods of NSDictionary.

Example 2-15 shows how to work with mutable dictionaries.

Traditionally, a for-loop is used to enumerate the contents of a collection, which provides access to each member by its index. Since the for-loop technique depends on indexed collection contents, it won’t work for non-indexed collections, such as sets and dictionaries. NSEnumerator provides an object-oriented way of iterating over the contents of any collection. Each Foundation collection type implements the method objectEnumerator, which returns an enumerator for the receiver.

To illustrate how NSEnumerator is used in place of the for-loop, consider Examples Example 2-16 and Example 2-17. Example 2-16 shows how an array is traditionally enumerated using a for-loop.

Example 2-17 shows how the NSEnumerator class accomplishes the same task.

Some collection classes have variations on the standard objectEnumerator method. For example, the reverseObjectEnumerator method of NSArray lets you access the array’s contents from the last item to the first. Another variation is NSDictionary’s keyEnumerator method, which lets you enumerate the dictionary’s keys instead of its values.

Since the members of an array are indexed, expect an enumerator to return the contents of an array in a predictable order. NSDictionary and NSSet, on the other hand, don’t store their contents in a meaningful order, so the order in which the enumerators return the members is unpredictable.

Cocoa provides three classes to represent date and time information: NSDate, NSCalendarDate, and NSTimeZone. NSDate represents an instant in time, to millisecond precision, as the number of seconds since the absolute reference time, midnight (GMT), January 1, 2001. Many NSDate methods work with time intervals. A time interval is represented by the Foundation data type NSTimeInterval (which is a redefinition of the primitive type double). NSTimeIntervals specify a length of time in units of seconds. Example 2-19 shows how to use NSDate.

NSDate is a lightweight class that represents dates as points in time. NSCalendarDate, a subclass of NSDate, can additionally perform date arithmetic based on the Western Gregorian calendar. NSCalendarDate expands the functionality of NSDate to provide methods that work with dates in terms of days, weeks, months, and years. Example 2-20 summarizes what you can do with NSCalendarDate.

Associated with every NSCalendarDate object is an NSTimeZone object. Instances of NSTimeZone capture information about geographic time zones across the planet, such as their name, abbreviation, and “distance” from the reference time zone, GMT, in seconds. Additionally, NSTimeZone is aware of daylight savings time, and capable of translating dates between time zones. NSCalendarDate still stores a date in its lowest form as a time interval from the reference date, which is behavior it inherits from NSDate. NSTimeZone translates that time interval from GMT to a specific time zone. The systemTimeZone method used in Example 2-20 is just one method of NSTimeZone that returns the time zone set on your system. In addition to this method, NSTimeZone declares several other methods, some of which are shown in Example 2-21.

NSData encapsulates a buffer of bytes. Many Foundation framework classes have methods that let you initialize an object from an instance of NSData or convert the object’s contents into an NSData object. NSData is a generic object that lets you store and transport data of any kind, any way you like. Example 2-22 gives an example.

Note in the second line that despite initializing an NSData object with a C string, the NSData object is not a string. The data object has no idea what its contents represent, only that it is a collection of bytes. The client that interacts with the data object is responsible for knowing how to interpret the contents.

Like many other Foundation classes, NSData is an immutable class that has a mutable child class, NSMutableData. NSMutableData adds methods to change the length of the stored data (how many bytes are in there) and append data to the stored data, as illustrated in Example 2-23.