The Foundation framework provides support for a variety of basic functionalities and data types, including the following:
Strings, numbers, and collections
Dates and time
Binary data
Means of working with files, including accessing data and working with bundles
Distributed event notification
Operating system interaction
Threading
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.
Classes such as NSString
and
NSArray
are
immutable
classes; instances of these classes
cannot be altered after they are initialized. Each immutable class,
however, has a
mutable
subclass:
for example, NSString
has the mutable subclass
NSMutableString
, and NSArray
has the subclass NSMutableArray
. Mutable
subclasses extend their superclass’s functionality
to allow modification after initialization. Immutable classes are
more efficient, but mutable classes are more flexible.
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
NSRange
is a Foundation data type used to specify
a portion of a series. NSRange
is defined as:
typedef struct _NSRange { unsigned int location; unsigned int length; } NSRange;
The location is the starting index of the portion, and the length is
the number of elements of the series in the range. Methods that
return NSRange
s set the location of the range to
NSNotFound
to indicate an invalid range in the context of the operation.
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
"α
β
γ
δ
∊“:
Example 2-2. Working with Unicode strings and NSString objects
// Create the unichar string "
α β γ δ ∊"
unichar uc[5] = {0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5};// Initialize an NSString with a Unicode string
NSString *uStr = [NSString stringWithCharacters:&uc length:5];// Copy the Unicode characters into a buffer
unichar uc2[5] = [uStr characterAtIndex:0];
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.
Example 2-3. Breaking a string up into its components
// A sample record from some record set
NSString *rec = @"John:Doe:Austin:TX:etc";// Break the string into components separated by colons
// Returns the array {John, Doe, Austin, TX, etc}
NSArray *fields = [str componentsSeperatedByString:@":"];// NSArray can be used to rejoin the components into one string
// Returns "John*Doe*Austin*TX*etc"
NSString *rec2 = [fields componentsJoinedByString:@"*"];
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
.
Example 2-4. Using NSMutableString
// Create a mutable string from an immutable string
NSString *str = @"Hello, World"; NSMutableString *ms = [NSMutableString stringWithString:str];// Append one string to another, ms is now "Hello, World!"
[ms appendString:@"!"];// Insert strings within a string
// ms is now "He_garbage_llo, World!"
[ms insertString:@"_garbage_" atIndex:2];// Delete part of a string, ms is now "Hello, World!"
[ms deleteCharactersInRange:NSMakeRange(2,9)];// Replace part of a string with another string
// ms is now "Hello, World."
[ms replaceCharactersInRange:NSMakeRange(12,1) withString:@"."];// Replace the contents of a string with another string
[ms setString:@"That's all for now."];
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 id
s 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, |
):
NSCaseInsensitiveSearch
Makes the comparison case insensitive.
NSLiteralSearch
Compares the two strings on a byte-by-byte, rather than
character-by-character, basis. This comparison can improve speed for
some operations, but differing literal sequences may not match when
they otherwise would. For example, accented characters may be
represented by a composite character (e.g.,
é
), or a combined sequence of two
Unicode characters (e.g., e
and
´
).
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.
Example 2-5. Comparing strings
NSString *a = @"Right";// Test for equality; returns YES
BOOL v = [a isEqualToString:@"Right"];// Determine lexical order of two strings; returns NSOrderedSame
NSComparisonResult r = [a compare:@"Right"];// Returns NSOrderedDescending; light comes before Right
r = [a compare:@"light"];// Returns NSOrderedAscending; sight comes after Right
r = [a compare:@"sight"];// Literal, case-insensitive comparison by setting options
r = [a compare:@"right" options:NSCaseInsensitiveSearch | NSLiteralSearch];// Easier case-insensitive comparison; returns NSOrderedSame
r = [@"next" caseInsensitiveCompare:@"NeXT"];
NSAttributedString
provides an API for text strings that
contain information about graphical
attributes
of the text, such as its font, color, size, and kerning. Attributes
can be applied to individual characters, ranges of characters, or to
the entire length of the string. Like NSString
,
NSAttributedString
is an immutable class with a
mutable subclass, NSMutableAttributeString
.
The functionality of NSAttributedString
as it
exists in the Foundation framework is fairly basic.
Foundation’s functionality is limited to keeping
track of the string contents, as well as the various sets of
attributes that apply to different ranges of the string. The
Application Kit provides most functionality of
NSAttributedString
related to drawing and
displaying text, and is covered more in Chapter 3 and Chapter 4.
In addition to a rich abstraction for strings,
Foundation includes two classes that
support string processing: NSScanner
and
NSCharacterSet
.
An
NSCharacterSet
represents a collection of Unicode
characters. A number of sets are predefined and accessible through
class methods, including:
alphanumericCharacterSet
capitalizedLetterCharacterSet
controlCharacterSet
decimalDigitCharacterSet
letterCharacterSet
punctuationCharacterSet
whitespaceAndNewlineCharacterSet
whitespaceCharacterSet
You can also create a new character set from a string, using
characterSetWithCharactersInString
:, load in a set
from a file with characterSetWithContentsOfFile
:,
or invert an existing set with invertedSet
:.
NSCharacterSet
’s mutable
subclass, NSMutableCharacterSet
, allows you to,
amongst other modifications, add or remove string characters to or
from a set and form a union or intersection with another set. Mutable
character sets are, however, less efficient than immutable character
sets. If you do not need to change a character set after establishing
it, create an immutable copy with copy
, and use
that.
You would typically use NSCharacterSet
s to group
characters, to let you find part of a particular set when searching
an NSString
object. You might use
NString
’s
rangeOfCharacterFromSet:options:range
: method (or
a variant thereof) to find the range in the receiver of the first (or
in the case of a backwards search, last) character found from the set
argument. NSCharacterSets
are also used
extensively with NSScanner
.
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:
scanString:intoString
:
scanUpToString:intoString
:
scanCharactersFromSet:intoString
:
scanUpToCharactersFromSet:intoString
:
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
.
Example 2-6. Using NSScanner and NSCharacterSet
NSCharacterSet * letterSet , *whiteSet; letterSet = [NSCharacterSet letterCharacterSet]; whiteSet = [NSCharacterSet whitespaceAndNewlineCharacterSet]; NSString *filePath, *fileString; NSScanner *scanner; filePath = [@"~/scannerTest.txt" stringByExpandingTildeInPath]; fileString = [NSString stringWithContentsOfFile:filePath]; scanner = [NSScanner scannerWithString:fileString]; while ( ![scanner isAtEnd] ) { NSString *fName, *lName; int empId; if ( [scanner scanString:@"EmpId: " intoString:nil] ) { [scanner scanInt:&empId]; [scanner scanString:@"FirstName: " intoString:nil]; [scanner scanCharactersFromSet:letterSet intoString:&fName]; [scanner scanString:@"LastName: " intoString:nil]; [scanner scanCharactersFromSet:letterSet intoString:&lName]; NSLog(@"%@ %@, EmpID: %d", fName, lName, empId); [scanner scanCharactersFromSet:whiteSet intoString:nil]; } }
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
NSNumber
s.
Example 2-7. Working with NSNumber
// NSNumbers can contain any primitive C type
NSNumber *iN = [NSNumber numberWithInt:1]; NSNumber *fN = [NSNumber numberWithFloat:50.5f]; NSNumber *dN = [NSNumber numberWithDouble:100.45]; NSNumber *cN = [NSNumber numberWithChar:100]; NSNumber *lN = [NSNumber numberWithLong:100]; NSNumber *usN = [NSNumber numberWithUnsignedShort:30];// Access the value of an NSNumber object
int i = [iN intValue];// Returns 1
float f = [fN floatValue];// Returns 50.5
double d = [dN doubleValue];// Returns 100.45
char c = [cN charValue];// Returns 100
long l = [lN longValue];// Returns 100
unsigned short us = [usN unsignedShortValue];// Returns 30
// Test for equality of two numbers; returns YES
BOOL b = [nc isEqualToNumber:nl];// Determine how one number compares to another in order
NSComparisonResult r = [nc compare:nus];// NSOrderedDescending
r = [nus compare:ns];// NSOrderedAscending
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
.
Example 2-8. Working with NSDecimalNumber
// Planck's Constant (1.04e-34)
NSDecimalNumber *h = [NSDecimalNumber decimalNumberWithManitssa:104 exponent:-36 isNegative:NO];// NSDecimalNumber has methods for returning commonly used numbers
NSDecimalNumber *one = [NSDecimalNumber one];// 1.0
NSDecimalNumber *zero = [NSDecimalNumber zero];// 0.0
// NSNumbers that represent system limits
NSDecimalNumber *max = [NSDecimalNumber maximumDecimalNumber]; NSDecimalNumber *min = [NSDecimalNumber minimumDecimalNumber];// Methods to operate on the numbers
NSDecimalNumber *n; n = [one decimalNumberByAdding:zero];// n = 1.0
n = [one decimalNumberBySubtracting:zero];// n = 1.0
n = [h decimalNumberByMultiplyingBy:c];// n = 3.16e-26
n = [h decimalNumberByDividingBy:one];// n = 1.05e-34
n = [c decimalNumberByRaisingToThePower:2];// n = 9.0e16
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
:
NSArray
Stores an ordered, immutable collection of objects, where each member is referenced by its index number. Any given object may appear in an array more than once.
NSSet
Stores an immutable, unordered collection of unique objects, which
support the mathematical idea of a set. NSSet
objects are useful when your collection requires you to test an
object for membership; NSSet
provides a more
efficient implementation for testing object membership over that of
other collection classes.
NSDictionary
Stores a collection of objects as key-value pairs; each member has an associated key that identifies that member object.
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.
Example 2-9. Creating and working with NSArray objects
// Create an array from several objects
// Objects are separated by commas, and the list must end with nil
NSArray *a = [NSArray arrayWithObjects:@"Hello",@"how",@"are", @"you",nil];// If you need an array with one object
NSArray *b = [NSArray arrayWithObject:@"One object"];// Create an array from the contents of an XML property list
b = [NSArray arrayWithContentsOfFile:@"/path/to/plist"];// Test arrays for equality
BOOL r = [a isEqualToArray:b];// Returns NO
// Determine the number of memebers in a collection
int n = [a count];// Returns 4
// Access elements of the array
NSString *one = [a objectAtIndex:0];// Returns @"Hello"
NSString *end = [a lastObject];// Returns @"you"
// Discover the index of an object
unsigned idx = [a indexOfObject:@"how"];// Returns 1
// Find out if an array contains some object
BOOL result = [a containsObject:@"today"];// Returns NO
// Obtain a new array by adding an object
NSArray *newA = [a arrayByAddingObject:@"today"];// Extract subarrays
NSArray *subA = [a subarrayWithRange:NSMakeRange(1,2)];
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.
Example 2-10. A sampling of NSMutableArray methods
// Create a mutable array from an immutable array
NSMutableArray *ma = [NSMutableArray arrayWithArray:a];// Create an empty mutable array
NSMutableArray *ma = [NSMutableArray array];// Exercise mutability
[ma addObject:@"World"];// ma is {World}
[ma insertObject:@"Hello" atIndex:0];// ma is {Hello, World}
[ma removeObjectAtIndex:0];// ma is {World}
[ma removeLastObject];// ma is {}
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-11. Using NSSet
// Create a set from the contents of an array
NSSet *set1 = [NSSet setWithArray:anArray];// Create a set from arbitrary objects
set1 = [NSSet setWithObjects:@"a", @"b", @"c",@"d", nil];// Create a set from a single object
NSSet *set2 = [NSSet setWithObject:@"a"];// Determine the size of the set
unsigned int n = [set1 count];// Returns 4
// Access set members; creates an NSArray from the set contents
NSArray *setObjs = [set1 allObjects];// You can have a set randomly (essentially) return a member
id object = [set1 anyObject];// Test for membership, the strength of NSSet; returns YES
BOOL b = [set1 containsObject:@"a"]; b = [set1 containsObject:@"z"];// Returns NO
id mem = [set1 member:@"a"];// Returns @"a"
id mem = [set1 member:@"z"];// Returns nil
// Compare two sets
NSSet *set3 = [NSSet setWithObjects:@"c", @"d", @"e", @"f", nil]; BOOL b = [set2 isSubsetOf:set1];// Returns YES
b = [set2 intersectsSet:set1];// Returns YES
b = [set3 intersectsSet:set1];// Returns NO
b = [set1 isEqualToSet:set2];// Returns NO
Example 2-12 shows what
NSMutableSet
adds to NSSet
.
Example 2-12. Methods provided by NSMutableSet
// Add and remove member objects
[set1 addObject:@"e"];// set1 now [a, b, c, d, e]
[set1 removeObject:@"a"];// set1 now [b, c, d, e]
[set2 removeAllObjects];// set1 now []
// Combine sets
[set1 unionSet:set3];// set1 now [b, c, d, e, f]
[set1 minusSet:set3];// set1 now [b]
[set1 intersectSet:set3];// set1 now []
[set1 setSet:set3];// set1 now [c, d, e, f]
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
.
Example 2-13. Methods provided by NSCountedSet
// Add and remove objects; inherits methods from NSMutableSet
[set3 addObject:@"b"];// set3 now [b, c, d, e, f]
[set3 addObject:@"b"];// Increments count for b to 2
[set3 addObject:@"b"];// Count for b now 3
[set3 countForObject:@"b"];// Returns 3
[set3 removeObject:@"b"]; [set3 countForObject:@"b"];// Returns 2
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-14. Working with NSDictionary
// Create an empty dictionary, useful for
// creating empty mutable dictionaries
NSDictionary *d = [NSDictionary dictionary];// Initialize a dictionary with contents of an XML property list
d = [NSDictionary dictionaryWithContentsOfFile:@"pList"];// Create a dictionary from one object with a key
d = [NSDictionary dictionaryWithObject:@"a" forKey:@"A"];// Create a dictionary with many objects and keys
d = [NSDictionary dictionaryWithObjects:@"a", @"b", nil forKeys:@"A", @"B", nil];// Count the number of objects in the dictionary;
int n = [d count];// Returns 2
// Access objects and keys;
id obj = [d objectForKey:@"A"];// Returns "a"
// Returns nil since "a" is not a valid key
obj = [d objectForKey:@"a"];// Returns an array whose members are the keys of the receiver
NSArray *k = [d allKeys];// Returns an array with the dictionary's objects
NSArray *v = [d allValues];// Returns an enumerator for the receiver's keys
NSEnumerator *e = [d keyEnumerator];// Returns enumerator for objects in dictionary
e = [d objectEnumerator];// Write contents of dictionary to a file formatted
// as an XML property list
[d writeToFile:@"/path/to/file" atomically:YES];
Example 2-15 shows how to work with mutable dictionaries.
Example 2-15. Working with NSMutableDictionary
// Create a mutable dictionary from an immutable dictionary
NSMutableDictionary *md; md = [NSMutableDictionary dictionaryFromDictionary:d];// Add a key-value pair
[md setObject:@"c" forKey:@"C"];// Remove an object from the dictionary
[md removeObjectForKey:@"A"];// You can also remove all objects in one fell swoop
[md removeAllObjects];// Finally, replace the current contents with the
// contents of another dictionary
[md setDictionary:d];
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-16. Using a for-loop to enumerate an array’s contents
// Assume NSArray *array exists
int i; id object; for ( i = 0; i < [array count]; i++ ) { object = [array objectAtIndex:i];// Do something with the object
}
Example 2-17 shows how the
NSEnumerator
class accomplishes the same task.
Example 2-17. Using NSEnumerator to enumerate an array’s contents
// Assume NSArray *array exists
NSEnumerator *e = [array objectEnumerator]; id object; while ( object = [e nextObject] ) {// Do something with the object
}
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.
Whenever an object is added to a
collection, the collection object
sends that object a retain
message, asserting some
ownership over the object that is now a member of the collection.
This is true whether the object is added as part of the collection
initialization, or at a later point with the
addObject
:-based methods in mutable collection
classes. Objects that are removed from collections receive a
release
message, as the collection no longer has
any interest in maintaining ownership over the object. When a
collection is deallocated, all member objects are sent a release
message. Example 2-18 shows how this works in
practice.
Example 2-18. Collection memory management
// anObject has reference count of 1
id anObject = [[ObjectClass alloc] init];// Assume anArray is an existing mutable array; reference
// count of anObject is now 2.
[anArray addObject:anObject];// anObject reference count now 1, still valid because of
// retain sent by the array in addObject:
[anObject release];// Either of these actions will cause anObject to be released
[anArray removeObject:anObject]; [anArray release];
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
).
NSTimeInterval
s specify a length of time in units
of seconds. Example 2-19 shows how to use
NSDate
.
Example 2-19. Fundamental methods of NSDate
// Create an NSDate set to the current date
NSDate *today = [NSDate date];// Obtain a date that is many centuries in the future
NSDate *future = [NSDate distantFuture];// Similarly, obtain a date that is many centuries in the past
NSDate *past = [NSDate distantPast];// A date that is some number of seconds past the system reference
// date (or before if you supply a negative value)
NSDate *intvl = [NSDate dateWithTimeIntervalSinceReferenceDate:60];// Check for equality of two dates; returns NO
BOOL b = [today isEqualToDate:intvl];// These methods return either the earlier or
// the later of the two dates involved.
NSDate *d = [today earlierDate:past];// Returns past
NSDate *d = [today laterDate:future];// Returns future
// Obtain Time Intervals
NSTimeInterval d = [intvl timeIntervalSinceReferenceDate];// Number of seconds between receiver date and current date
d = [today timeIntervalSinceNow];// Number of seconds between the two dates
d = [today timeIntervalSinceDate:[NSDate date]];// Number of seconds since 1970, another reference date
d = [today timeIntervalSince1970];
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
.
Example 2-20. Working with NSCalendarDate
// Create an NSCalendarDate
NSCalendarDate *cd = [NSCalendarDate calendarDate];// Create an arbitrary calendar date
cd = [NSCalendarDate dateWithYear:2002 month:4 day:10 hour:20 minute:3 second:0 timeZone:[NSTimeZone systemTimeZone]];// Retrieve elements of a calendar date
int dce = [cd dayOfCommonEra];// Returns 730950
int dm = [cd dayOfMonth];// Returns 10
int dw = [cd dayOfWeek];// Returns 3
int d = [cd dayOfYear];// Returns 100
int h = [cd hourOfDay];// Returns 20
int m = [cd minuteOfHour];// Returns 3
int s = [cd secondOfMinute];// Returns 0
int y = [cd yearOfCommonEra];// Returns 2002
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.
Example 2-21. Working with NSTimeZone
// Create time zone objects
NSTimeZone *tz = [NSTimeZone timeZoneWithAbbreviation:@"CST"];// Obtain the geo-political name of the time zone
// Returns "America/Chicago"
NSString *name = [tz name];// Get the time zone's abbreviation; Returns CST
NSString *abv = [tz abbreviation];// Returns whether or not it is daylight saving time; returns NO
BOOL b = [tz isDaylightSavingTime];// The time difference relative to GMT in seconds; Returns -18000
int s = [tz secondsFromGMT];
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.
Example 2-22. Working with NSData
// NSData objects can be created to hold the contents of any data
// buffer, such as a static C character string
char *cData = "This is data, a string of bytes"; NSData *data = [NSData dataWithBytes:cData length:strlen(cData)];// Create NSData objects from files
data = [NSData dataWithContentsOfFile:@"/path/to/file"];// Create data objects from resources located by NSURL objects
data = [NSData dataWithContentsOfURL:URLObject];// Get a C pointer to the data object contents
void *p = [data bytes];// Copy the contents of data object into a buffer
char buffer[50]; [data getBytes:(void *)buffer];// Copy a specified number of bytes into the buffer
[data getBytes:buffer length:4];// Copy a range of bytes from the data object into the buffer
[data getBytes:buffer range:NSMakeRange(5,2)];// Determine the number of bytes in the data
unsigned l = [data length];
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.
Example 2-23. Working with NSMutableData
// Create an empty NSData object
NSMutableData *mData = [NSMutableData data];// Set the size of the internal NSData buffer to 29 bytes
[mData setLength:29];// Take the data from a buffer and place it
// into the NSData object
[mData appendBytes:cData length:29];