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.
// 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 "abgd":
// Create the unichar string "abgd" 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];
|
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.
// 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.
// 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 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, |):
Makes the comparison case insensitive.
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.
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 NSCharacterSets 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.
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 NSNumbers.
// 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.
// 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:
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.
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.
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.
// 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.
// 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.
// 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.
// 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.
// 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.
// 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.
// 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.
// 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.
// 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.
// 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). NSTimeIntervals specify a length of time in units of seconds. Example 2-19 shows how to use 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.
// 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.
// 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.
// 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.
// 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];