IOS4 note 11 (四)
Equality and Comparison
The foregoing types will quickly come to seem to you like basic data types, but of course they are actually object types. Therefore you cannot compare them with the C operators for testing equality as you would with actual numbers. That’s because, in the case of object types, the C operators compare the pointers, not the object content of the instances.
Similarly, it is up to individual classes to supply ordered comparison methods. The standard method is called compare:, and returns one of three constants:
NSOrderedAscending (the receiver is less than the parameter),
NSOrderedSame (the receiver is equal to the parameter),
NSOrderedDescending (the receiver is greater than the parameter).
NSIndexSet
NSIndexSet expresses a collection of ordered integers (so, despite the name, it isn’t really a set, because a set is unordered). For example, you might want to speak of elements 1, 2, 3, 4, 8, 9, and 10 of an NSArray. NSIndexSet expresses this notion in some compact implementation that can be readily queried. The actual implementation is opaque, but you can imagine that in this case the set might consist of two NSRange structs, (1,4) and (8,3). NSIndexSet is thus very commonly used with NSArray. For example, to retrieve multiple objects simultaneously from an array, you specify the
desired indexes as an NSIndexSet. It is also used with other things that are array-like; for example, you pass an NSIndexSet to a UITableView to indicate what sections to insert or delete.
An NSIndexSet is immutable; its mutable subclass is NSMutableIndexSet. You can form a simple NSIndexSet consisting of just one contiguous range directly, by passing an NSRange to indexSetWithIndexesInRange:; but to form a more complex index set you’ll need to use NSMutableIndexSet so that you can append additional ranges.
Walking through (enumerating) the index values specified by an NSIndexSet is easy in iOS 4.0, which provides enumerateIndexesUsingBlock:. But if your code is to run on earlier systems, you can’t use blocks, and no enumerator is provided, so you must resort to a rather clumsy construct (Example 10-2).
Example 10-2. Enumerating an NSIndexSet
NSIndexSet* ixen = //...;
NSUInteger ix = [ixen firstIndex];
do {
// ... do something with ix ...
} while ((ix = [ixen indexGreaterThanIndex:ix]) != NSNotFound);
NSArray and NSMutableArray
An NSArray is an ordered collection of objects. Its length is its count, and a particular object can be obtained by index number using objectAtIndex:.
You can walk through (enumerate) every object in an array with the for...in construct described in Chapter 1. (You’ll get an exception if you try to mutate an array while enumerating it.)
You can seek an object within an array with indexOfObject: or indexOfObjectIdenticalTo:; the former’s idea of equality is to call isEqual:, whereas the latter uses pointer equality.
Those familiar with other languages may miss such utility array functions as map, which builds a new array of the results of calling a method on each object in the array. (makeObjectsPerformSelector: requires a selector that returns no value, and enumerateObjectsUsingBlock: requires a block function that returns no value.) The usual workaround is to make an empty mutable array and then enumerate the original array, calling a method and appending each result to the mutable array. It is also sometimes possible to use key–value coding as a map substitute.
You can filter an array to produce a new array consisting of just those objects meeting a test that can be described as an NSPredicate:
NSArray* pep = [NSArray arrayWithObjects: @"Manny", @"Moe", @"Jack", nil];
NSPredicate* p = [NSPredicate predicateWithFormat:@"self BEGINSWITH[cd] 'm'"];
NSArray* ems = [pep filteredArrayUsingPredicate:p];
To filter an array on a more customized test, you can walk through the array applying the test and adding those that meet it to an NSMutableArray. Alternatively, in iOS 4.0 there is now the ability to filter an array using a block:
NSArray* pep = [NSArray arrayWithObjects: @"Manny", @"Moe", @"Jack", nil];
NSArray* ems =
[pep objectsAtIndexes: [pep indexesOfObjectsPassingTest:
^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return ([(NSString*)obj rangeOfString:@"m"
options:NSCaseInsensitiveSearch].location == 0);
}]];
You can derive a sorted version of the array, supplying the sorting rules in various ways, or if it’s a mutable array, you can sort it directly.