Showing posts with label objective-c_tuesdays. Show all posts
Showing posts with label objective-c_tuesdays. Show all posts

Tuesday, December 6, 2011

Objective-C Tuesdays: more NSArray sorting

Welcome to another Objective-C Tuesdays. Last week, we looked at sorting C arrays and NSArrays. Today, we will continue looking at sorting NSArrays using NSSortDescriptors.

As we saw last week, the sorting methods of NSArray require you to specify a comparator in one form or another. When sorting an NSArray of simple objects like NSStrings or NSDates, the comparators are usually pretty simple to write and common objects often have useful comparator methods like -caseInsensitiveCompare: and -localizedCompare:.

When sorting NSArrays of more complex objects, writing comparators is often more tedious and error-prone. Here's the interface for simple Person class:
// Person.h
@interface Person : NSObject

@property (strong) Address *address;
@property (strong) NSDate *birthdate;
@property (copy) NSString *firstName;
@property (copy) NSString *lastName;

@end

And here's the Address class used by Person:
// Address.h
@interface Address : NSObject

@property (copy, nonatomic) NSString *street;
@property (copy, nonatomic) NSString *city;
@property (copy, nonatomic) NSString *state;
@property (copy, nonatomic) NSString *country;
@property (copy, nonatomic) NSString *postalCode;

@end

If we have an NSArray of Person objects, we may want to sort them in country, lastName, firstName order. Here's one way to do that, using a comparator block:
// sort Person objects by lastName, firstName
Person *frodo = [Person new];
[frodo setFirstName:@"Frodo"];
[frodo setLastName:@"Baggins"];
// ...
[[frodo address] setCountry:@"Shire"];

Person *bilbo = [Person new];
[bilbo setFirstName:@"Bilbo"];
[bilbo setLastName:@"Baggins"];
// ...
[[bilbo address] setCountry:@"Shire"];

Person *legolas = [Person new];
[legolas setFirstName:@"Legolas"];
[legolas setLastName:@"Greenleaf"];
// ...
[[legolas address] setCountry:@"Mirkwood"];

NSArray *people = [NSArray arrayWithObjects:frodo, bilbo, legolas, nil];
NSArray *sortedPeople = [people sortedArrayUsingComparator:^(id item1, id item2) {
Person *person1 = item1;
Person *person2 = item2;

// NSComparisonResult is a typedef for int
NSComparisonResult result = [[[person1 address] country] compare:[[person2 address] lastName]];
if (result) {
return result;
}

result = [[person1 lastName] compare:[person2 lastName]];
if (result) {
return result;
}

result = [[person1 firstName] compare:[person2 firstName]];
if (result) {
return result;
}

return NSOrderedSame; // NSOrderedSame == 0
}];
// sortedPeople contains:
// Legolas Greenleaf (Mirkwood)
// Bilbo Baggins (Shire)
// Frodo Baggins (Shire)

The general pattern of a multi-field comparator is simple: check each field in turn, stop and return the comparison result if non-zero; if all fields are equal, return zero (or NSOrderedSame to be more descriptive). This quickly becomes tedious when you have many fields to sort by or you need to dig down into child or grandchild objects for fields.

Fortunately, there's an easier way to do this. NSArray has a method called -sortedArrayUsingDescriptors: that takes an array of NSSortDescriptor objects. Each NSSortDescriptor specifies a key path and sort direction (ascending or descending). The order of NSSortDescriptors in the array determines the precedence of each field. If you're not familiar with Key Value Coding (KVC), you may not have encountered key paths before. KVC is similar reflection in Java and other dynamic languages. KVC allows you to get and set fields on an object using the field names as strings, called keys. To access fields on child objects, you use keys separated by dots to form a key path; KVC knows how to drill down your object graph and access fields on child objects. There are a lot of interesting things you can do with KVC, but today we will stick to building an array of NSSortDescriptors:
NSSortDescriptor *byCountry = [NSSortDescriptor sortDescriptorWithKey:@"address.country" 
ascending:YES];
NSSortDescriptor *byLastName = [NSSortDescriptor sortDescriptorWithKey:@"lastName"
ascending:YES];
NSSortDescriptor *byFirstName = [NSSortDescriptor sortDescriptorWithKey:@"firstName"
ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:byCountry, byLastName, byFirstName, nil];

Notice that the byCountry sort descriptor uses the key path @"address.country": it will first get the value of the address property of the Person object, then get the country property of the address. Key paths can be as deep as your object graph.

Using the array of sort descriptors is easy:
NSArray *sortedPeople = [people sortedArrayUsingDescriptors:sortDescriptors];
// sortedPeople contains:
// Legolas Greenleaf (Mirkwood)
// Bilbo Baggins (Shire)
// Frodo Baggins (Shire)

This certainly makes creating complex sort criteria much easier, and you're not limited to the default comparator for a field. You can specify a selector for a comparator method on the field this way:
// specify a method to call on the lastName object
NSSortDescriptor *byLastName = [NSSortDescriptor sortDescriptorWithKey:@"lastName"
ascending:YES
selector:@selector(caseInsensitiveCompare:)];

Or for more specialized comparisons, you can pass in a NSComparator block this way:
// sort descriptor using length of last name
NSSortDescriptor *byLastNameLength = [NSSortDescriptor sortDescriptorWithKey:@"lastName"
ascending:YES
comparator:^(id item1, id item2) {
NSString *lastName1 = item1;
NSString *lastName2 = item2;
// cast result to NSComparisonResult so that the
// compiler infers the correct return type
return (NSComparisonResult) ([lastName1 length] - [lastName2 length]);
}];

Specifying complex sort orders with NSSortDescriptors is the type of higher level, declarative code that is easy to write, easy to read and easy to maintain, and in most cases you should consider using NSSortDescriptor rather than writing your own comparator methods, functions or blocks.

Next time, we will look at sorting NSMutableArrays in place, rather than producing a sorted copy like the various -sortedArray methods.

Tuesday, November 29, 2011

Objective-C Tuesdays: sorting arrays

Good news everyone! Objective-C Tuesdays is back! We left off a few weeks ago talking about techniques for inserting and removing items in arrays. Today we will cover sorting C arrays as well as NSArrays and NSMutableArrays.

Sorting C arrays with qsort()
The C standard library includes only one built-in way to sort C arrays: the qsort() function. qsort() is an implementation of the quicksort algorithm, a good general purpose sorting algorithm. qsort() sorts an array in place.

The qsort() prototype looks like this:
qsort(void *array, size_t itemCount, size_t itemSize,
int (*comparator)(void const *, void const *));

If you're not experienced using function pointers in C, the declaration of qsort() will look confusing. That's because declaring a C function pointer type is confusing. I'll use a typedef to make things easier to understand:
// alternate declaration of qsort using a typedef:
typedef int (*Comparator)(void const *, void const *);

qsort(void *array, size_t itemCount, size_t itemSize,
Comparator comparator);

Function pointers are declared in the same way that functions are declared in C, except they have a * before the function name like other pointer variables do. But there's one hitch. In a function pointer declaration, the * is ambiguous; by default, C will assume the * is part of the return type of the function. So when you declare a function pointer, you need a set of parentheses around the * and the function name in order to clue in the compiler.
// declare a function returning a pointer to an int
int *returnAPointerToSomeInt(void);
// which is interpreted as:
// (int *)returnAPointerToSomeInt(void);

// declare a pointer to a function returning an int
int (*returnSomeInt)(void);

Now that we've cleared that up, let's look at the declaration of qsort() again.
typedef int (*Comparator)(void const *, void const *);

qsort(void *array, size_t itemCount, size_t itemSize,
Comparator comparator);

The first three parameters of qsort() specify the array to sort, the count of items in the array and the size (in bytes) of a single item. Note that the array is of type void *, which allows us to pass any type of C array into qsort().

The last parameter is the comparator function.
typedef int (*Comparator)(void const *item1, void const *item2);

The comparator takes pointers to two items from the array and returns a negative number if item1 belongs before item2, a positive number if item1 belongs after item2 and zero if the two items are equal (or have equivalent ordering). This style of comparator function is used in many languages for sorting. I think of the items positioned on a number line when trying to understand a comparator:

item1 <=====<< item2
<-+------+-----+------+------+->
-2 -1 0 1 2

item2 >>=====> item1
<-+------+-----+------+------+->
-2 -1 0 1 2

When item1 belongs before item2, the comparator function will return a negative number; when item1 belongs after item2, the comparator returns a positive number. This relationship is the difference between item1 and item2, or item1 - item2.

The C standard library doesn't include many comparator functions, but they're easy to write. Here's an example of a comparator for ints to sort them in natural order:
int compareInts(void const *item1, void const *item2) {
int const *int1 = item1;
int const *int2 = item2;
return *int1 - *int2;
}

We create local variables int1 and int2 since a void pointer will automatically convert to a typed pointer in C, and we know that qsort() will actually be giving us pointers to ints. The two parameters are also const, so we can't modify the int values that the items point to (which would cause qsort() to go haywire). Then we can simply return the difference between the two int values to determine their sort order. We could do this in one line using casts and parentheses:
int compareInts(void const *item1, void const *item2) {
return *( (int const *) item1) - *( (int const *) item2);
}

Either way, now we can use our compareInts() function to sort an array of ints:
int array[6] = { 42, 17, 57, 19, 11, 5 };

qsort(array, 6, sizeof(int), compareInts);
// array now contains 5, 11, 17, 19, 42, 57

Now suppose we wanted to sort our array in reverse order? We would still use qsort() to do the job, but employ a different comparator function. Here is compareIntsReversed():
int compareIntsReversed(void const *item1, void const *item2) {
int const *int1 = item1;
int const *int2 = item2;
return -(*int1 - *int2);
// or simply return *int2 - *int1;
}

By reversing the sign of the comparator's return value, you can reverse the order of sorting.
int array[6] = { 42, 17, 57, 19, 11, 5 };

qsort(array, 6, sizeof(int), compareIntsReversed);
// array now contains 57, 42, 19, 17, 11, 5

The qsort() function is very flexible. You can sort arrays of complex types as easily as arrays of ints. To sort an array of C strings in lexicographic order, use the strcmp() function from the C standard library.
char const *array[3] = { "red", "green", "blue" };

qsort(array, 3, sizeof(char const *), strcmp);
// array now contains "blue", "green", "red"

Sorting an array of CGPoints, first write a comparator:
int compareCGPoints(void const *item1, void const *item2) {
struct CGPoint const *point1 = item1;
struct CGPoint const *point2 = item2;

if (point1->x < point2->x) {
return -1;
} else if (point1->x > point2->x) {
return 1;
}

if (point1->y < point2->y) {
return -1;
} else if (point1->y > point2->y) {
return 1;
}

return 0;
}

Notice that we first compare the X coordinates of the points and only check the Y coordinates if the X coordinates are equal. This is a common pattern when sorting over multiple fields in a struct. Here's an example of the CGPoint comparator in action:
struct CGPoint pointArray[4] = {
{ 4.0f, 3.0f },
{ 2.0f, 1.0f },
{ 4.0f, 1.0f },
{ 2.0f, 3.0f }
};
qsort(pointArray, 4, sizeof(struct CGPoint), compareCGPoints);
// pointArray now contains:
// { 2.0f, 1.0f }
// { 2.0f, 3.0f }
// { 4.0f, 1.0f }
// { 4.0f, 3.0f }


Other C sorting functions
For most common sorting jobs, qsort() it a reasonable choice, and it's the only choice that's included in the C standard library. OS X and iOS also include a couple of other sorting functions worth mentioning: heapsort() and mergesort(), which have the same parameters as qsort() and are detailed on the qsort() man page. heapsort() is slower than qsort() but uses a limited amount of memory while sorting, whereas qsort() uses recursion and can potentially overflow the stack when sorting huge arrays. mergesort() can be significantly faster than qsort() when the array is mostly in sorted order already, but is significantly slower when the array is in random order.

Sorting an NSArray
Objective-C provides a number of ways to sort an NSArray. Because NSArray objects are immutable, all the sorting methods on NSArray return a new sorted NSArray object and leave the original one unchanged. The most basic sorting method is -sortedArrayUsingFunction:context:, which is similar to sorting a C array with qsort(). The full method declaration looks like this:
- (NSArray *)sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))comparator 
context:(void *)context

The first parameter is a pointer to a comparator function. As we did for qsort(), I'll rewrite the method declaration using a typedef for the function pointer to make it a little easier to read:
typedef NSInteger (*Comparator)(id item1, id item2, void *context);

- (NSArray *)sortedArrayUsingFunction:(Comparator)comparator
context:(void *)context

The comparator function for NSArray sorting has a few differences from the qsort() one. It returns an NSInteger, which is simply a typedef for int. Instead of two items of type void const *, it takes two items of type id since NSArrays can only hold object types. And finally, there's an extra context parameter, a void pointer that allows you to pass extra information to the comparator if you need to (but you can safely ignore it if you don't need it).

Let's write a comparator function that orders NSStrings by their length.
static NSInteger compareStringsByLength(id item1, id item2, void *context) {
return [item1 length] - [item2 length];
}

Since we can send any message to a variable of type id, we don't even need casts or intermediate variables here. (Of course, we'll get a runtime error if we put the wrong type of objects into our array, but comparators for qsort() have the similar problems.)

And now let's see it in action:
NSArray *array = [NSArray arrayWithObjects:@"Florida", 
@"Texas", @"Mississippi", @"Delaware", nil];

NSArray *sortedArray = [array sortedArrayUsingFunction:compareStringsByLength
context:NULL];
// sortedArray contains:
// @"Texas"
// @"Florida"
// @"Delaware"
// @"Mississippi"

Now let's use the context parameter to make it easy to switch between normal and reversed sort order. We can pass a pointer to any kind of data we want, so we will use a pointer to a BOOL, where a YES value means reversed and a NO value means normal ordering.
NSInteger compareStringsByLength(id item1, id item2, void *context) {
BOOL *reversed = context;
NSInteger order = [item1 length] - [item2 length];
if (*reversed) {
return -order;
} else {
return order;
}
}

Now we need to pass something to the context parameter when we call -sortedArrayUsingFunction:context:
NSArray *array = [NSArray arrayWithObjects:@"Florida", 
@"Texas", @"Mississippi", @"Delaware", nil];

BOOL reversed = YES;
NSArray *sortedArray = [array sortedArrayUsingFunction:compareStringsByLength
context:&reversed];
// sortedArray contains:
// @"Mississippi"
// @"Delaware"
// @"Florida"
// @"Texas"

Note that we use the & operator to pass the address of the reversed variable as the context.

If you target iOS 3.2, OS X 10.6 or later, you can use the block version, -sortedArrayUsingComparator:. Blocks make one-off comparator functions easier to read and understand by putting the comparator definition right along side the sort method call. We can rewrite our example this way:
NSArray *array = [NSArray arrayWithObjects:@"Florida", 
@"Texas", @"Mississippi", @"Delaware", nil];

BOOL reversed = YES;
NSArray *sortedArray = [array sortedArrayUsingComparator:^(id item1, id item2) {
NSInteger order = [item1 length] - [item2 length];
if (reversed) {
return -order;
} else {
return order;
}
}];
// sortedArray contains:
// @"Mississippi"
// @"Delaware"
// @"Florida"
// @"Texas"

Since a block copies the value of variables like reversed in the enclosing scope at the time it's created, there's no need for a second context parameter to -sortedArrayUsingComparator:. Blocks are really handy, but if you find yourself writing the same comparator block in multiple places in your code, you might be better off using a plain old comparator function and -sortedArrayUsingFunction: instead to prevent duplicate code, in keeping with the DRY principle. (Or you might consider how to restructure your code so that sorting is defined in only one place.)

Since items in an NSArray are all objects, very often the items themselves have a useful comparator method. When this is the case, the -sortedArrayUsingSelector: method is very handy. NSArrays of NSStrings are very common, as is the need to sort in a case-insensitive manner. Using NSString's -caseInsensitiveCompare: method, we can sort this way:
NSArray *tagNames = [NSArray arrayWithObjects:@"H1", 
@"body", @"A", @"Head", nil];

NSArray *sortedTagNames = [tagNames sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
// sortedTagNames contains:
// @"A"
// @"body"
// @"H1"
// @"Head"

There's one more interesting way to sort an NSArray: we'll look at the -sortedArrayUsingDescriptors: method and the NSSortDescriptor class next week.

Tuesday, August 2, 2011

Objective-C Tuesdays: more about dynamic arrays

Welcome back to Objective-C Tuesdays. Last time we were talking about dynamic arrays in C and NSMutableArray in Objective-C. We'll continue looking at both today.

We looked at using malloc() or calloc() to dynamically allocate a block of memory that you can treat like an array. This is very useful when you don't know the number of items you need to store until run time or when you need the array to exist longer than the current function call.

But what happens when you need to grow or shrink the memory block? Well, the straightforward way would be to allocate a new memory block, copy over the contents and free the old block. You use the memcpy() function in the C standard library to do the copying.
// resizing a memory blockint *lotteryNumbers = malloc(sizeof(int) * 6);if ( ! lotteryNumbers) {  // memory allocation failed  exit(EXIT_FAILURE);}lotteryNumbers[0] = 7;// ...lotteryNumbers[5] = 29;// now we need to store more numbers// so allocate a bigger memory blockint *biggerMemoryBlock = malloc(sizeof(int) * 12);if ( ! biggerMemoryBlock) {  // memory allocation failed  exit(EXIT_FAILURE);}// copy lotteryNumbers into biggerMemoryBlockmemcpy(biggerMemoryBlock, lotteryNumbers, sizeof(int) * 6);// free old memory blockfree(lotteryNumbers);// replace old memory block pointer with new onelotteryNumbers = biggerMemoryBlock;lotteryNumbers[6] = 31;// ...lotteryNumbers[11] = 49;
When copying blocks of memory, be careful to specify the correct size in bytes. In the example above, we use the same expression to calculate the size, sizeof(int) * 6 for both the original call to malloc() and to memcpy(). Using the sizeof operator like this serves two purposes. It makes the code portable, since the size of an int may be different on some systems (it's four bytes on iOS). More importantly, it tells anyone reading the code that you intended to allocate six ints. If you had specified simply malloc(24) or even malloc(6 * 4), your intention is not as clear.

Resizing a memory block with realloc()
Allocating a larger or smaller memory block and copying over the contents works perfectly, but it is tedious. Fortunately, the C standard library includes the realloc() function which does exactly this for you in one function call. It can operate on any memory block allocated with malloc() or calloc(). In addition, because it's part of the standard library, realloc() can do things that you can't: it will try to grow or shrink the memory block in place, skipping the copy step. Sometimes the memory manager will give you a block of memory somewhat bigger than you requested, other times the memory area after your memory block is currently unused. Because realloc() is privy to the implementation details of the memory manager, it can figure out when it's safe to grow your memory block in place.

The realloc() function returns a pointer to either the original memory block or a new memory block on success, or NULL if memory allocation fails. If the new memory block is bigger than the original, the new items will be set to zero, just as if you allocated the memory block with calloc(). Like malloc(), realloc() requires you to calculate the size of the memory block in bytes.
// using realloc()int *lotteryNumbers = malloc(sizeof(int) * 2);if ( ! lotteryNumbers) {  // out of memory  exit(EXIT_FAILURE);}lotteryNumbers[0] = 7;lotteryNumbers[1] = 11;// ...int *resizedLotteryNumbers = realloc(lotteryNumbers, sizeof(int) * 6);if ( ! resizedLotteryNumbers) {  // out of memory but lotteryNumbers memory block  // is still valid and unchanged  exit(EXIT_FAILURE);}// replace the old pointer with the new onelotteryNumbers = resizedLotteryNumbers;lotteryNumbers[2] = 19;// ...
If realloc() fails, it leaves the original memory block unchanged. Just as for calloc() and malloc(), you must remember to call free() on the memory block when you're done with it.

Inserting items into a C array
It should be obvious by now how to add items to the end of a C array (if it's big enough) or a dynamically allocated memory block that you've enlarged using realloc(). But how do you insert an item at the beginning or middle of an array while keeping all of the current items? There's no magic here, you have to shift items to higher indices to make space for the new one.
// insert an item at the start of a C arrayint lotteryNumbers[6] = { 11, 17 }; // has room for six items// shift current items uplotteryNumbers[2] = lotteryNumbers[1];lotteryNumbers[1] = lotteryNumbers[0];// array now holds { 11, 11, 17 }lotteryNumbers[0] = 7; // insert new item// array now holds { 7, 11, 17 }
Note that you need to shift items starting at the end of the array, otherwise you'll overwrite items you want to preserve. Obviously writing a line of code for each item you need to shift isn't a practical solution. You might be tempted to use the memcpy() function to shift items, but memcpy() has an important restriction: the source and destination memory blocks may not overlap. (This is because memcpy() may copy items front to back and thus overwrite items you want to preserve.) The memmove() function is meant to be used when the memory blocks overlap and will always shift items in the correct order to preserve the contents.
// using memmove() to insert an itemint lotteryNumbers[6] = { 11, 17 }; // has room for six items// shift current items upint *destination = lotteryNumbers + 1;int *source = lotteryNumbers;size_t size = sizeof(int) * 2;memmove(destination, source, size);// array now holds { 11, 11, 17 }lotteryNumbers[0] = 7; // insert new item// array now holds { 7, 11, 17 }
Because memmove() can operate on any kind of array or memory block, we need to do some pointer arithmetic to determine the source and destination addresses and the number of bytes to move. Again, the sizeof operator helps make our intentions clear. An alternate way specify the source and destination addresses is to use the address of operator (&) on indexed items in the array.
// determine source and destination using address of// ...int *destination = &lotteryNumbers[1];int *source = &lotteryNumbers[0];size_t size = sizeof(int) * 2;memmove(destination, source, size);// array now holds { 11, 11, 17 }// ...
These are equivalent notations and choosing one over the other is a matter of style.

You can use this technique to insert items into both C arrays and dynamically allocated memory blocks. Just make sure that there is enough space to move items around and that you don't write items past the end of your array or memory. As with all things in C, there is no safety net. To remove items from the beginning or middle of an array, follow the same procedure using memmove(), only shift items down and intentionally overwrite the items you want to delete. I'll leave this one as an exercise for the reader ☺.

Inserting items into an NSMutableArray
While using memmove() to shift items around isn't too terrible, the equivalent operations using NSMutableArray are naturally much more direct. To insert a single item at a particular index, you naturally use -insertObject:atIndex:
// inserting an item into an NSMutableArrayNSMutableArray *colors = [NSMutableArray arrayWithObjects:@"red",                                                           @"green",                                                           @"blue",                                                           nil];[colors insertObject:@"orange" atIndex:1];// colors now holds "red", "orange", "green" and "blue"
Inserting multiple items at one time is trickier. With the -insertObjects:atIndexes: method, you can insert items from an NSArray into your NSMutableArray. The tricky part here is that you need to also pass in an NSIndexSet (or NSMutableIndexSet) along with the array. Here's how you would insert an NSArray containing one item.
// inserting an item into an NSMutableArrayNSMutableArray *colors = [NSMutableArray arrayWithObjects:@"red",                                                           @"green",                                                           @"blue",                                                           nil];NSArray *newColors = [NSArray arrayWithObject:@"orange"];NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:1];[colors insertObjects:newColors atIndexes:indexSet];// colors now holds "red", "orange", "green" and "blue"
You must make sure that there is an index in the NSIndexSet for each item you are inserting. If you want to insert the new items all in one spot, the +indexSetWithIndexesInRange: method of NSIndexSet makes this easy.
// inserting multiple items into an NSMutableArrayNSMutableArray *colors = [NSMutableArray arrayWithObjects:@"red",                                                           @"green",                                                           @"blue",                                                           nil];NSArray *rainbowColors = [NSArray arrayWithObjects:@"orange",                                                    @"yellow",                                                    @"indigo",                                                    @"violet",                                                    nil];NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 4)];[colors insertObjects:rainbowColors atIndexes:indexSet];// colors now holds "red", "orange", "yellow", "indigo", "violet", "green" and "blue"
Sometimes you want to insert different items at different points in your NSMutableArray. This is where -insertObjects:atIndexes: really shines. You'll want to use NSMutableIndexSet to create the index set in this case. Lets modify the previous example to insert the colors in standard rainbow order.
// inserting multiple items into an NSMutableArrayNSMutableArray *colors = [NSMutableArray arrayWithObjects:@"red",                                                           @"green",                                                           @"blue",                                                           nil];NSArray *rainbowColors = [NSArray arrayWithObjects:@"orange",                                                    @"yellow",                                                    @"indigo",                                                    @"violet",                                                    nil];NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];[indexSet addIndex:1];[indexSet addIndex:2];[indexSet addIndex:5];[indexSet addIndex:6];[colors insertObjects:rainbowColors atIndexes:indexSet];// colors now holds "red", "orange", "yellow", "green", "blue", "indigo" and "violet"
To figure out the correct indices can be tricky. Inside the -insertObjects:atIndexes: method, new items will be inserted one at a time, starting with the lowest index in the index set. While inserting a group of items is a very powerful capability of NSMutableArray, often times I think it's easier and faster to write correct code that uses -subarrayWithRange: to break the items up into contiguous blocks before inserting them, or even to use -insertObject:atIndex: repeatedly to insert items one by one. But when it is straightforward to figure out the index set, -insertObjects:atIndexes: makes your code very succinct.

Next time, we'll look at sorting C arrays, NSArrays and NSMutableArrays.

Tuesday, July 26, 2011

Objective-C Tuesdays: Dynamic arrays

Another week, another Objective-C Tuesdays. Last week we began our series on data structures with a look at arrays in C and the NSArray class. Both C arrays and NSArray objects have serious limitations: C arrays are fixed in size and NSArrays are immutable. Today we will look at overcoming those limitations.

Dynamically allocated memory
C arrays are fixed in size when they are declared:
int lotteryNumbers[6];
Here we declare an array that can hold six ints. We can freely change the six int values we store in the array, but we can't make the array larger or smaller once it's declared. But there's nothing really magical about arrays in C: in essence they're blocks of memory managed by the compiler. When we declare an array, we tell the compiler the number of items we need to store and the type of item and it calculates the number of bytes of memory it needs to set aside for the array. We can do the same thing with a dynamically allocated memory block. Let's dynamically allocate the same amount of storage using malloc():
// dynamically allocating an array
#include <stdlib.h>

int *lotteryNumbers = malloc(sizeof(int) * 6);
if (lotteryNumbers) {
  lotteryNumbers[0] = 7;
  lotteryNumbers[1] = 11;
  // ...
}
The malloc() function is part of the standard C library, and it allocates a memory block on the heap. As we saw last time, you can use the same square bracket index notation with an array variable or a pointer to a block of memory.

Unlike global variables (which persist for the whole life span of your program) and local variables (which live only as long as the current function call), you control the life span of memory you allocate on the heap. Just as you need to match -retain with -release in Objective-C, you need to match calls to malloc() with corresponding calls to free():
// always free dynamically allocating arrays
#include <stdlib.h>
// ...

int *lotteryNumbers = malloc(sizeof(int) * 6);

// use lotteryNumbers for a while...

free(lotteryNumbers);
The malloc() function takes one argument: the number of bytes to allocate. Since most useful items require more than one byte each, you need to use the sizeof() operator to get the size of the item type and multiply it by the number of items required.
// calculate the number of bytes required
// using the sizeof() operator
int *lotteryNumbers = malloc(sizeof(int) * 6);
if (lotteryNumbers) {
  // ...
The malloc() function returns a pointer to the newly allocated memory block on success. If malloc() fails, it returns NULL. You should always check the result of memory allocation and take appropriate action. The typical C idiom is to use the returned pointer as a boolean value, since NULL pointers in C evaluate to false while non-NULL pointers are true, similar to nil values in Objective-C.
// always test the returned pointer
int *lotteryNumbers = malloc(sizeof(int) * 6);
if (lotteryNumbers) {
  // okay to use...
} else {
  // we're out of memory...
}

Handling malloc() failures
If a call to malloc() fails and returns NULL, it's almost always because you've run out of available memory. There are two broad strategies for coping with a memory allocation failure: fail fast or abort the operation. In general, I recommend that you fail fast by doing something like this:
// fail fast when out of memory

int *lotteryNumbers = malloc(sizeof(int) * 6);
if ( ! lotteryNumbers) {
  fprintf(stderr, "%s:%i: Out of memory\n", __FILE__, __LINE__);
  exit(EXIT_FAILURE);
}
// okay to use memory
lotteryNumbers[0] = 7;
// ...
In an Objective-C program, you would use NSLog() instead of fprintf(). When small to medium size memory allocations fail, the system is seriously constrained and there's not much else your program can do to cope. In fact, iOS will likely terminate your app before you ever reach this condition.

Sometimes your program is trying to do something particularly memory intensive, like editing a large image or sound file. In cases like this, you should be prepared for large memory allocations to fail and try to abort the operation gracefully. The strategy in this case is to free all resources allocated for the operation so far and alert the user.

Using calloc() instead of malloc()
When you dynamically allocate a memory block, you frequently want to set all the items to zero. malloc() doesn't do any initialization to the memory block, so the initial contents are effectively random garbage. The calloc() function is similar to malloc(), but also clears the bytes in the memory block to zeros before returning.
// using calloc()
size_t itemCount = 6;
size_t itemSize = sizeof(int);
int *lotteryNumbers = calloc(itemCount, itemSize);
if (lotteryNumbers) {
  // okay to use...
Unlike malloc() which takes the size of the memory block in bytes, calloc() takes the number of items and the size of each item and does the math for you. Under the covers, calloc() allocates memory from the same heap that malloc() uses, so you need to call free() on the memory block when you're done.

There's a lot more to managing dynamically allocated memory blocks. We'll look at resizing a memory block using realloc() next time but for now let's move on to a more pleasant topic: NSMutableArray.

NSMutableArray
Like its immutable super class NSArray, the NSMutableArray class takes mutable array management to a higher level. You can create an NSMutableArray the same way you create an NSArray:
NSMutableArray *colors = [NSMutableArray arrayWithObjects:@"red", 
                                                          @"green", 
                                                          @"blue", 
                                                          nil];
Another common creation technique is to duplicate an existing immutable NSArray using the +arrayWithArray: or -initWithArray: methods.
NSArray *rgbColors = [NSArray arrayWithObjects:@"red", 
                                               @"green", 
                                               @"blue", 
                                               nil];
NSMutableArray *colors = [NSMutableArray arrayWithArray:rgbColors];
Often, you simply want an empty array to start with. The +array or -init methods from NSArray will do the trick here. (You can create empty immutable NSArray objects this way too, they're just usually not very useful.)

Adding items to the end of the array is easily done with -addObject: and -addObjectsFromArray:
NSMutableArray *colors = [NSMutableArray array];
// colors is empty

[colors addObject:@"yellow"];
[colors addObject:@"purple"];
// colors holds yellow, purple

NSArray *designerColors = [NSArray arrayWithObjects:@"mauve", 
                                                    @"chartreuse", 
                                                    @"seafoam", 
                                                    nil];
[colors addObjectsFromArray:designerColors];
// colors now holds yellow, purple, mauve, chartreuse and seafoam

NSMutableArray has many ways to remove objects. The -removeLastObject method is the inverse of -addObject:. The -removeObjectAtIndex: method removes an item at a particular index. Continuing with our array of colors:
// colors holds yellow, purple, mauve, chartreuse and seafoam
[colors removeLastObject];
// colors holds yellow, purple, mauve and chartreuse
[colors removeObjectAtIndex:0];
// colors holds purple, mauve and chartreuse

That covers the basics of adding and removing objects from an NSMutableArray. Next time, we'll cover more ways to manipulate the mutable array contents.

Tuesday, July 19, 2011

Objective-C Tuesdays: arrays

Welcome back to Objective-C Tuesdays. Last time we wrapped up our series on strings by looking at regular expressions in Objective-C. Today we begin a new series: data structures. The first data structure that we will examine is the array.

Most languages have some concept of an array, though it is sometimes called a list. In general, an array is an ordered sequence: a collection of items that has a distinct order. The term "array" implies that each item in the collection is individually accessible in constant time; in other words, it takes the same amount of time to access items at the beginning, middle or end of the sequence.

Arrays in C
The C language includes the ability to define and create strongly typed arrays. You must always declare the type of the items that the array contains.
// declare an array of ints
int lotteryNumbers[6];
This declares an array that holds six integers. When the array variable is declared, the number between the square brackets indicates the number of items that the array can hold, usually referred to as the size, length or count.

C99 (which is the default for iOS projects) allows you to use a function parameter or other variable to determine the length of an array. This feature is naturally called "variable length arrays".
// use a variable as the length of an array
int length = 6;
int lotteryNumbers[length];
Unless you're an old time C programmer, you're probably thinking "yeah, so what?" In earlier versions of C, you could only declare arrays with constant length. In old C code (or code written old C hands), it's common to see code like this:
// constant array length
#define LOTTERY_NUMBERS_LENGTH 6

int lotteryNumbers[LOTTERY_NUMBERS_LENGTH];

C array initialization
You can optionally provide an array with an initializer. An array initializer uses curly braces and looks like this:
// array initializer
int lotteryNumbers[6] = { 7, 11, 19, 23, 29, 31 };
You can specify fewer items than the array can hold; the remaining items will be initialized to zero.
// array with partial initializer
int lotteryNumbers[6] = { 7, 11 };
You can even specify an empty initializer and all the items in the array will be set to zero.
// array with empty initializer
int lotteryNumbers[6] = {};
This is redundant for arrays declared at global scope since global variables are initialized to zero by default, but can be useful for local variables.

If you use an initializer list when you declare your array, you can leave the array length out of the declaration:
// array initializer without length
int lotteryNumbers[] = { 7, 11, 19, 23, 29, 31 };
The compiler will count the items in the initializer list and size your array to fit.

If you are initializing an array of chars, you can use a string literal as the initializer.
char favoriteColor[4] = "red";
char favoriteFlavor[] = "vanilla";
Remember that C strings contain an extra char, the null terminator, so when you initialize an array of chars with the string "red", it actually stores four items. You can make the equivalent initializers using character literals:
char favoriteColor[4] = { 'r', 'e', 'd', 0 };
char favoriteFlavor[] = { 'v', 'a', 'n', 'i', 'l', 'l', 'a', '\0' };

Accessing items in an array
You get items out of an array by using an array index number in square brackets:
int lotteryNumbers[6] = { 7, 11, 19, 23, 29, 31 };

NSLog(@"%i is at index 1", lotteryNumbers[1]);
NSLog(@"%i is at index 2", lotteryNumbers[2]);
The code snippet above produces this output:
11 is at index 1
19 is at index 2
If this is surprising, it's because the first item in a C array is always at index 0.

Assigning items to an array is naturally very similar:
int lotteryNumbers[6] = { 7, 11, 19, 23, 29, 31 };

lotteryNumbers[1] = 13;
lotteryNumbers[2] = 17;
NSLog(@"%i is at index 1", lotteryNumbers[1]);
NSLog(@"%i is at index 2", lotteryNumbers[2]);
which will print out:
13 is at index 1
17 is at index 2

Arrays automatically convert to pointers
Like all things in C, arrays are very low level constructs. Under the hood, an array is simply a block of memory managed by the compiler that's large enough to hold all its items. Because an array corresponds directly to a memory block, an array variable will automatically convert into a pointer to the first item in the array.
// automatic array to pointer conversion
int lotteryNumbers[6] = { 7, 11, 19, 23, 29, 31 };
int *luckyNumber = lotteryNumbers;

NSLog(@"My lucky number is %i", *luckyNumber
This will produce the output:
My lucky number is 7
You can set the pointer to items after the first by using pointer arithmetic:
// pointer arithmetic
int lotteryNumbers[6] = { 7, 11, 19, 23, 29, 31 };
int *luckyNumber = lotteryNumbers;

NSLog(@"My NEW lucky number is %i", *(luckyNumber + 1)
which prints out the
My NEW lucky number is 11
While pointer arithmetic is a perfectly cromulent way to access items in an array, you can use an index in square brackets on a pointer just as you can on an array:
// array index using a pointer variable
int lotteryNumbers[6] = { 7, 11, 19, 23, 29, 31 };
int *luckyNumber = lotteryNumbers;

NSLog(@"My NEW lucky number is %i", luckyNumber[1]
Under the hood, the compiler automatically converts uses of an array variable into a pointer to the first item in the array, then converts array index expressions into the equivalent pointer arithmetic. An expression like myArray[2] is converted to *(myArray + 2), or a pointer to the third item in the array. (Remember that the first item is myArray[0].)

While this is a very convenient way to work with low level memory in a structured way, it can also be very dangerous. The compiler won't stop you from accessing items past the end of your array. You can easily and efficiently read memory that may contain garbage values and overwrite memory belonging to other parts of your program. As with many things in C, with great power comes great responsibility.

Calculating the length of an array
The sizeof operator can be applied to an array variable to find out how many bytes of memory the array occupies.
// size of an array in bytes
int lotteryNumbers[6] = { 7, 11, 19, 23, 29, 31 };

NSLog(@"The array uses %lu bytes", sizeof lotteryNumbers);
This produces:
The lotteryNumbers array uses 24 bytes
ints in iOS use four bytes each, so an array of six ints uses 24 bytes. (The sizeof operator returns a value of type size_t, an unsigned long integer type.) To get the number of items the array contains, you can divide the size of the array by the size of its first item:
// size of an array in bytes
int lotteryNumbers[6] = { 7, 11, 19, 23, 29, 31 };

size_t length = sizeof lotteryNumbers / sizeof lotteryNumbers[0];
NSLog(@"The array contains %lu items", length);

Be careful to only use this on actual array variables—you won't get the answer you expect if you try this on a pointer.

C arrays have one big limitation: you can't resize them. We'll talk about using dynamically allocated memory blocks as arrays next time.

NSArray
If you're ready for a safer, higher level way to manage an ordered sequence of items, it's time to get to know NSArray.

You can create an NSArray containing one item using +arrayWithObject: or -initWithObject:
// creating an NSArray with one item
NSArray *colors = [NSArray arrayWithObject:@"red"];
NSArray *flavors = [[NSArray alloc] initWithObject:@"vanilla"];
NSArray creation methods follow the common Cocoa and Cocoa Touch conventions. Class methods like +arrayWithObject: return a new autoreleased object. If you need to hold on to the object beyond the current method, remember to call -retain on it. Instance methods like -initWithObject: produce new objects that you own—don't forget to call -release or -autorelease on the object when you're done with it.

Sometimes an array containing one item is handy, but usually you want to hold onto multiple items. To do that, use +arrayWithObjects: or -initWithObjects::
// creating an NSArray with multiple items
NSArray *colors = [NSArray arrayWithObjects:@"red", 
                                            @"green", 
                                            @"blue", 
                                            nil];
NSArray *flavors = [[NSArray alloc] initWithObjects:@"vanilla", 
                                                    @"chocolate", 
                                                    @"strawberry", 
                                                    nil];
The +arrayWithObjects: or -initWithObjects: methods take a variable number of arguments, but have a special caveat: you must mark the end of the list with nil. If you forget the nil, your program will probably crash with an EXC_BAD_ACCESS error as it tries to add random memory locations to the NSArray. Fortunately, the LLVM 2.0 compiler in Xcode 4.0 will warn you if you forget the nil. Always pay attention to compiler warnings!

If you have a plain old C array of object pointers, you can use the +arrayWithObjects:count: or -initWithObjects:count methods to create an NSArray from the C array.
// creating an NSArray from a C array
NSString *colors1[] = { @"red", @"green", @"blue" };
NSArray *colors2 = [NSArray arrayWithObjects:colors1 count:3];

NSString *flavors1[] = { @"vanilla", @"chocolate", @"strawberry" };
NSArray *flavors2 = [[NSArray alloc] initWithObjects:flavors1 count:3];

Accessing an item in an array is done with the -objectAtIndex: method.
// my favorite color is green
NSArray *colors = [NSArray arrayWithObjects:@"red", 
                                            @"green", 
                                            @"blue", 
                                            nil];

NSLog(@"My favorite color is %@", [colors objectAtIndex:1]);
You can ask an NSArray object how many items it contains using the -count method.
NSArray *favoriteColors = [NSArray arrayWithObject:@"green"];
NSArray *favoriteFlavors = [NSArray arrayWithObjects:@"vanilla", @"chocolate", nil];

NSLog(@"I have %u favorite color(s) and %u favorite flavor(s)", 
      [favoriteColors count], [favoriteFlavors count]);

Only for objects, but heterogeneous
NSArray has one big limitation: it can only contain object types. If you try to create an NSArray of ints or a similar primitive C type, you'll get a warning like "Incompatible integer to pointer conversion sending 'int' to parameter of type 'id'". If you need to store numbers in an NSArray, you can store NSNumber objects instead.
// wrap primitive types in objects to store them in an NSArray
NSNumber *one = [NSNumber numberWithInt:1];
NSNumber *pi = [NSNumber numberWithDouble:3.14];
NSDate *today = [NSDate date];
NSString *foo = [NSString stringWithContentsOfCString:"foo" encoding:NSUTF8Encoding];

NSArray *myStuff = [NSArray arrayWithObjects:one, pi, today, foo, nil];
This example shows an interesting characteristic of NSArrays: they can hold items of many different object types in a single container. While occasionally this feature is useful, more often than not you'll only store items of one type in a given NSArray, and errors related to finding an unexpected type in your NSArrays are pretty rare.

NSArrays are immutable
Like plain old C arrays, once you create an NSArray, you can't change its size. But NSArrays are even more restrictive; you can't change the contents either. NSArrays are immutable. This is something of a pain in the rump, but it means that you can safely share an NSArray between threads, as long as the items in it are also immutable.

Next time, we'll look at using memory blocks as resizable C arrays, and NSArray's more flexible cousin, NSMutableArray.

Objective-C Tuesdays: strings in Objective-C

Last week we wrapped up our survey of strings by examining regular expressions in Objective-C. Here's a quick overview of the posts that cover strings:

String basics:

String operations:

Data structures is the next topic, which we start today.

Tuesday, July 12, 2011

Objective-C Tuesdays: regular expressions

Welcome back to Objective-C Tuesdays after a long hiatus. In the last couple of entries, we looked at searching and replacing in C strings and NSStrings. Today we'll look at a more powerful way to search and replace in strings: regular expressions.

A mini language
Regular expressions is a small, specialized programming language for matching text. In addition to being specialized in scope, regular expressions are also very compact, which can make them very hard to read. I won't cover regular expression grammar in dept here—there are plenty of regular expression tutorials, references and cheat sheets out there. If you're looking for a good reference, I recommend O'Reilly's Mastering Regular Expressions and the Regular Expressions Cookbook.

Some programming languages have first class support for regular expressions, including Perl, Ruby and JavaScript. Modern languages like Python and Java support regular expressions as part of the language's standard library. Unfortunately C doesn't have regular expression support as part of its standard library. For Objective-C, regular expression support first appeared with iOS 3.2 on iPad and 4.0 on iPhone. Before that, developers needed to use a third party library to use regular expressions in their apps.

Some simple examples
Most characters in regular expressions match themselves, but some characters have special meaning. Here's a simple regular expression that matches the word "foobar":
foobar
To match "foo" followed by one 'b' character, you could use:
foob?
Here, the '?' character is a special character that modifies the expression before it, (in this case the character 'b') and matches it zero or one times. Thus foob? will match "foo" or "foob" but not "foe".

To match "foo" followed by zero or more 'b' characters, you could use:
foob*
The '*' special character matches the preceding expression zero or more times. foob* will match "foo", "foob" and "foobbbbbb" but not "fod".

To match "foo" followed by one or more 'b' characters, you could use:
foob+
The '+' special character matches the preceding expression one or more times. foob+ will match "foob" and "foobbbbbb" but not "foo" or "food".

Regular expression languages have many more capabilities. Though different regular expression implementations have different capabilities, most share a large set of common operations,.

C libraries for regular expressions
The PCRE or Perl Compatible Regular Expressions library is a widely used C library that implements the same regular expression language that's used in Perl 5. The PCRE library is open source and distributed under a BSD license. It's a substantial library, and due to the lack of support on iOS for OS X style frameworks, it can be a bit challenging to include in an iOS project. In addition, because it's a C library, working with PCRE requires more low-level code than many Cocoa Touch programmers are comfortable with.

Here's a PCRE code snippet that compiles the regular expression foo(bar|fy) and tests it against the string "foofy".
#include 
#include 
#include 

#define OUTPUT_VECTOR_COUNT 30    /* should be a multiple of 3 */

/* ... */

char const *pattern = "foo(bar|fy)";
int compileOptions = 0;
char const *error;
int errorOffset;
unsigned char const *characterTable = NULL;
pcre *regularExpression = pcre_compile(pattern, compileOptions, 
                                       &error, &errorOffset, characterTable);

if ( ! regularExpression) {
  NSLog(@"ERROR: regular expression <%s> failed to compile\n", pattern);
  NSLog(@"  Error at offset %i: %s\n", errorOffset, error);
  exit(EXIT_FAILURE);
}

pcre_extra *extraData = NULL;
char const *subject = "foofy";
int subjectLength = strlen(subject);
int subjectOffset = 0;
int execOptions = 0;
int outputVector[OUTPUT_VECTOR_COUNT];
int execResultCount = pcre_exec(regularExpression, extraData, 
                                subject, subjectLength, subjectOffset, 
                                execOptions, outputVector, OUTPUT_VECTOR_COUNT);

if (execResultCount == PCRE_ERROR_NOMATCH) {
  NSLog(@"The subject <%s> did not match the pattern <%s>\n", subject, pattern);
} else if (execResultCount < 0) {
  NSLog(@"Unexpected pcre_exec() result %i\n", execResultCount);
} else if (execResultCount == 0) {
  NSLog(@"Output vector only has room for %i captured substrings\n", 
        execResultCount - 1);
} else {
  int resultIndex;
  
  NSLog(@"Found match for pattern <%s> in subject <%s> at offset %i\n", 
        pattern, subject, outputVector[0]);
  for (resultIndex = 0; resultIndex < execResultCount; ++resultIndex) {
    int substringIndex = 2 * resultIndex;
    int substringStartingOffset = outputVector[substringIndex];
    int substringEndingOffset = outputVector[substringIndex + 1];
    char const* substringStart = subject + substringStartingOffset;
    int substringLength = substringEndingOffset - substringStartingOffset;
    NSLog(@"match %i: <%.*s>\n", resultIndex, substringLength, substringStart);
  }
}

pcre_free(regularExpression);

/* ... */
As you can see, it takes a lot of boilerplate code to do even a simple regular expression search using PCRE, but it's a very powerful library used by many well knows open source and commercial projects, including Apple's Safari browser. The PCRE project also includes a set of C++ wrappers that make the library easier to use from that language. If you only plan to target OS X, there is also the very excellent RegexKit open source library that provides a nice Objective-C wrapper around PCRE. The PCRE library can be built with Unicode support, but is limited to scanning UTF-8 encoded Unicode strings, since it's a byte oriented library.

The POSIX Regular Expressions specification describes a regular expression dialect and C interface that is supported on many variants of Unix and Linux, including OS X and iOS. Like many specifications, POSIX regular expressions are a "lowest common denominator" solution and lack some of the advanced features found in regular expression variants like Perl and PCRE. The POSIX regular expression interface is much smaller than what PCRE provides: four functions, two structs and a bunch of constants. Here's a PCRE code snippet that compiles the regular expression foo(bar|fy) and tests it against the string "foofy" using the POSIX interface.
#import 

#define MATCH_COUNT 10

/* ... */

regex_t regularExpression;
char const *pattern = "foo(fy|bar)";
int compileFlags = REG_EXTENDED;

int compileResult = regcomp(&regularExpression, pattern, compileFlags);

if (compileResult) {
  size_t bufferSize = regerror(compileResult, &regularExpression, NULL, 0);
  char *buffer = malloc(bufferSize);
  if ( ! buffer) {
    NSLog(@"Memory allocation failed");
    return;
  }
  regerror(compileResult, &regularExpression, buffer, bufferSize);
  NSLog(@"%s", buffer);
  free(buffer);
  return;
}

char const *string = "foofy";
regmatch_t matches[MATCH_COUNT];
int executeFlags = 0;

int executeResult = regexec(&regularExpression, string, 
                            MATCH_COUNT, matches, executeFlags);

if (executeResult == REG_NOMATCH) {
  NSLog(@"Pattern <%s> doesn't match string <%s>\n", pattern, string);
} else {  
  NSLog(@"Pattern <%s> matches string <%s>\n", pattern, string);
  NSLog(@"Found %lu submatches\n", (unsigned long)regularExpression.re_nsub);
  for (size_t i = 0; i < regularExpression.re_nsub + 1; ++i) {
    int substringLength = matches[i].rm_eo - matches[i].rm_so;
    char const *substringStart = string + matches[i].rm_so;
    NSLog(@"submatch %lu: <%.*s>\n", (unsigned long)i, substringLength, substringStart);
  }
}

regfree(&regularExpression);

/* ... */
This is a little more concise than the PCRE version, mainly because the POSIX functions have fewer options, but is still very low level. POSIX regular expression implementations are generally regarded as slower than PCRE, and don't provide any explicit Unicode support (though you may be able to do regex matching against UTF-8 encoded strings if you're careful to keep your UTF-8 strings normalized).

The ICU or International Components for Unicode library is included as part of iOS. (ICU is open source and carries a non-restrictive license.) The ICU is a general purpose library for working with Unicode text which includes Unicode-aware regular expression support. It has two versions: one for Java and another for C and C++. The C/C++ version features a low level C function interface and a set of higher level C++ classes. I'll only look at the C interface to ICU here.

Because the ICU library is a general purpose Unicode library, most operations require that C strings be converted into ICU's Unicode text type, UChar *, which makes the ICU example the longest yet:
// compile regular expression
char const *pattern = "foo(bar|fy)";
uint32_t compileFlags = 0;
UParseError parseError;
UErrorCode errorCode = U_ZERO_ERROR;

URegularExpression* regularExpression = uregex_openC(pattern, compileFlags, 
                                                     &parseError, &errorCode);
if (errorCode) {
  NSLog(@"uregex_openC() failed: %li: %s", 
        (long)errorCode, u_errorName(errorCode));
  NSLog(@"  parse error at line %li, offset %li" ,
        (long)parseError.line, (long)parseError.offset);
  return;
}

// determine size of search text as ICU Unicode string
UChar *unicodeText = NULL;
int32_t unicodeTextCapacity = 0;
int32_t unicodeTextLength;
char const *utf8Text = "foofy";
int32_t utf8TextLength = -1; /* null terminated */

errorCode = U_ZERO_ERROR;
u_strFromUTF8(unicodeText, unicodeTextCapacity, &unicodeTextLength, 
              utf8Text, utf8TextLength, &errorCode);

if (errorCode != U_BUFFER_OVERFLOW_ERROR) {
  NSLog(@"Conversion to Unicode string failed: %li: %s", 
        (long)errorCode, u_errorName(errorCode));
  uregex_close(regularExpression);
  return;
}

// allocate buffer for search text ICU Unicode string
unicodeTextCapacity = unicodeTextLength + 1;
unicodeText = calloc(sizeof(UChar), unicodeTextLength);
if ( ! unicodeText) {
  NSLog(@"Memory allocation failed");
  uregex_close(regularExpression);
  return;
}

// convert search text to ICU Unicode string
errorCode = U_ZERO_ERROR;
u_strFromUTF8(unicodeText, unicodeTextCapacity, &unicodeTextLength, 
              utf8Text, utf8TextLength, &errorCode);

uregex_setText(regularExpression, unicodeText, unicodeTextLength, &errorCode);
if (errorCode) {
  NSLog(@"uregex_setText() failed: %li: %s", 
        (long)errorCode, u_errorName(errorCode));
  free(unicodeText);
  uregex_close(regularExpression);
  return;
}

// search for regular expression
int32_t startIndex = 0;
errorCode = U_ZERO_ERROR;
BOOL matchFound = uregex_find(regularExpression, startIndex, &errorCode);
if (errorCode) {
  NSLog(@"uregex_find() failed: %li: %s", 
        (long)errorCode, u_errorName(errorCode));
  free(unicodeText);
  uregex_close(regularExpression);
  return;
}

if (matchFound) {
  // get number of subgroup matches
  NSLog(@"Pattern <%s> matched string <%s>", pattern, utf8Text);
  errorCode = U_ZERO_ERROR;
  int32_t subgroupCount = uregex_groupCount(regularExpression, &errorCode);
  if (errorCode) {
    NSLog(@"uregex_groupCount() failed: %li: %s", 
          (long)errorCode, u_errorName(errorCode));
    free(unicodeText);
    uregex_close(regularExpression);
    return;
  }
  
  // enumerate subgroup matches
  NSLog(@"Matched %li subgroups", (long)subgroupCount);
  for (int32_t i = 0; i <= subgroupCount; ++i) {
    // get size of the subgroup
    UChar *subgroup = NULL;
    int32_t subgroupCapacity = 0;
    errorCode = U_ZERO_ERROR;
    int32_t subgroupLength = uregex_group(regularExpression, i, 
                                          subgroup, subgroupCapacity, 
                                          &errorCode);
    if (errorCode != U_BUFFER_OVERFLOW_ERROR) {
      NSLog(@"uregex_group() failed: %li: %s", 
            (long)errorCode, u_errorName(errorCode));
      break;
    }
    
    // allocate buffer to hold the subgroup
    subgroupCapacity = subgroupLength + 1;
    subgroup = calloc(sizeof(UChar), subgroupCapacity);
    if ( ! subgroup) {
      NSLog(@"Memory allocation failed");
      return;
    }
    
    // copy subgroup into buffer
    errorCode = U_ZERO_ERROR;
    uregex_group(regularExpression, i, subgroup, subgroupCapacity, &errorCode);
    
    // determine size of buffer to hold subgroup as UTF8 string
    char *utf8Subgroup = NULL;
    int32_t utf8SubgroupCapacity = 0;
    int32_t utf8SubgroupLength;
    errorCode = U_ZERO_ERROR;
    u_strToUTF8(utf8Subgroup, utf8SubgroupCapacity, &utf8SubgroupLength, 
                subgroup, subgroupLength, &errorCode);
    if (errorCode != U_BUFFER_OVERFLOW_ERROR) {
      NSLog(@"u_strToUTF8() failed: %li: %s", 
            (long)errorCode, u_errorName(errorCode));
      free(subgroup);
      break;
    }
    
    // allocate buffer to hold subgroup as UTF8 string
    utf8SubgroupCapacity = utf8SubgroupLength + 1;
    utf8Subgroup = calloc(sizeof(char), utf8SubgroupCapacity);
    if ( ! utf8Subgroup) {
      NSLog(@"Memory allocation failed");
      free(subgroup);
      break;
    }
    
    // convert subgroup to UTF8 string
    errorCode = U_ZERO_ERROR;
    u_strToUTF8(utf8Subgroup, utf8SubgroupCapacity, &utf8SubgroupLength, 
                subgroup, subgroupLength, &errorCode);
    if (errorCode) {
      NSLog(@"u_strToUTF8() failed: %li: %s", 
            (long)errorCode, u_errorName(errorCode));
      free(subgroup);
      free(utf8Subgroup);
      break;
    }
    
    // print subgroup UTF8 string
    NSLog(@"submatch %lu: <%.*s>\n", 
          (unsigned long)i, utf8SubgroupLength, utf8Subgroup);
    free(subgroup);
    free(utf8Subgroup);
  }
} else {
  NSLog(@"Pattern <%s> did not match string <%s>", pattern, utf8Text);
}

free(unicodeText);
uregex_close(regularExpression);
I wouldn't be surprised if there's a memory leak in there somewhere. I'm also no expert on this library, and I may be doing some things the hard way. If you're using C++, I'm sure a lot of this boilerplate code goes away. If you don't need or want to mix C++ into your iOS app, there are some Objective-C alternatives that are much more satisfying.

Regular expressions in Objective-C
In iOS 3.2, Apple added a new NSStringCompareOptions value for use in the various -rangeOfString:options: methods of NSString: NSRegularExpressionSearch. This option uses the regular expression support of the ICU library to do simple regular expression matching on an NSString object. Unfortunately, Apple only implemented a very minimal regular expression interface on NSString. You can search, but not replace, within an NSString and powerful regular expression features like subgroup matching are not exposed.
NSString *string = @"foofy";
NSString *pattern = @"foo(bar|fy)";

NSRange match = [string rangeOfString:pattern 
                              options:NSRegularExpressionSearch];

if (match.location == NSNotFound) {
  NSLog(@"Pattern <%@> doesn't match string <%@>", pattern, string);
} else {
  NSLog(@"Pattern <%@> matches string <%@> starting at location %lu",
        pattern, string, (unsigned long)match.location);
}

In iOS 4.0, Cocoa Touch gained the NSRegularExpression class, a full-featured regular expression processor built on top of the ICU regular expression library.
NSError *error;
NSString *pattern = @"foo(bar|fy)";

NSRegularExpression *regularExpression = [NSRegularExpression regularExpressionWithPattern:pattern 
                                                                                   options:0 
                                                                                     error:&error];
if ( ! regularExpression) {
  NSLog(@"Error in pattern <%@>: %@", pattern, error);
  return;
}

NSString *string = @"foofy";
NSRange range = NSMakeRange(0, [string length]);
NSArray *matches = [regularExpression matchesInString:string 
                                              options:0 
                                                range:range];
if ([matches count]) {
  NSTextCheckingResult *firstMatch = [matches objectAtIndex:0];
  NSLog(@"Found %lu submatches", (unsigned long)[firstMatch numberOfRanges]);
  for (NSUInteger i = 0; i < [firstMatch numberOfRanges]; ++i) {
    NSRange range = [firstMatch rangeAtIndex:i];
    NSString *submatch = [string substringWithRange:range];
    NSLog(@"submatch %lu: <%@>", (unsigned long)i, submatch);      
  }
} else {
  NSLog(@"Pattern <%@> doesn't match string <%@>", pattern, string);
}
This is much easier to use than the underlying ICU C library and a good choice for iOS apps that target 4.0 and later.

The RegexKitLite library provides an alternate Objective-C wrapper around the low level ICU regular expression library that supports both Mac OS X and iOS, including iOS 3.0. RegexKitLite provides a full featured set of regular expression methods in a category that extends NSString and can do matching, searching, replacing and supports subgroups. RegexKitLite is open source and available under a BSD license.
NSString *string = @"foofy";
NSString *pattern = @"foo(bar|fy)";

NSArray *submatches = [string captureComponentsMatchedByRegex:pattern];
if ([submatches count]) {
  NSLog(@"Found %lu submatches", (unsigned long)[submatches count]);
  for (NSUInteger i = 0; i < [submatches count]; ++i) {
    NSLog(@"submatch %lu: <%@>", (unsigned long)i, [submatches objectAtIndex:i]);      
  }
} else {
  NSLog(@"Pattern <%@> doesn't match string <%@>", pattern, string);
}
RegexKitLite code is a little more concise than the NSRegularExpression class, but comparable in power, since they're both built on top of ICU.

Which one to use?
Unless you're writing cross-platform code in C or need to target very old versions of iOS, I recommend you avoid using the C regular expression libraries. If you must, the POSIX regex functions are the easiest to get started with, but if you're targeting non-Apple platforms, watch out for implementation differences across platforms. Both PCRE and ICU are good cross-platform choices; choose ICU if you also need robust Unicode support, PCRE if you're mainly working with eight bit encodings.

On the Objective-C side, choosing NSRegularExpression or RegexKitLite is largely up to personal preference. NSRegularExpression is a no-brainer if you're targeting iOS 4.0 and later (but currently not supported on OS X). Adding RegexKitLite to your project is as easy as adding two files to Xcode and currently works on both Apple operating systems. And the -rangeOfString:options: method on NSString is handy for simple searches.

<eot /> (end of topic)
That concludes our look at strings in Objective-C. Data structures is our next topic.

Tuesday, October 5, 2010

Objective-C Tuesdays: replacing in strings

Welcome back to Objective-C Tuesdays! Today we follow closely on last week's topic of searching in strings with it's sibling, replacing in strings.

It's a nightmare in C
In our series on strings in Objective-C, we've usually started by looking at C strings then moved on to NSStrings. Today is no different. In most cases, using NSString is easier than doing the equivalent operation on C strings. When it comes to replacing characters in a string, using NSString is significantly easier and safer. The standard C library doesn't provide much support for doing common string replacement operations, so you have to implement them yourself. Because of all the manual memory management required when working with C strings, this code is very error prone -- writing off the end of a buffer and forgetting to add the null terminator are two very common types of errors you have to watch out for when working with C strings.

Replacing a character
The only replacement operation that's fairly straightforward on C strings is replacing a single character with another character. Since C strings are just pointers to arrays of chars, you simply calculate the pointer to the char you want to change, dereference the pointer and assign the new char value.

There are two variations of this. The first one uses array notation and the second pointer operations. In both examples below, we use the strdup() function to make a copy of our original C string. The strdup() function isn't part of the C standard library, but most systems have one available (possibly named _strdup()) and it's easy to write one if it's missing on your system (it's available on iOS). You own the string returned by strdup() and are responsible for calling free() when you're done with it.

Here's how you change a character in a C string by treating it as an array of chars:
char const *source = "foobar";

char *copy = strdup(source); // make a non-const copy of source

copy[3] = 'B';               // change char at index 3

NSLog(@"copy = %@", copy);
// prints "copy = fooBar"

free(copy);                  // free copy when done
The alternative way uses pointer arithmetic:
char const *source = "foobar";

char *copy = strdup(source); // make a non-const copy of source

char *c3 = copy + 3;         // get pointer to char at index 3

*c3 = 'B';                   // change char at address of c3

NSLog(@"copy = %@", copy);
// prints "copy = fooBar"

free(copy);                  // free copy when done
As far as the compiler is concerned, this is basically the same code so use whichever method makes the most sense. If you know the index of the char you want to change, use array notation. If you already have a pointer to the char, perhaps from calling strchr(), use the pointer directly.

Replacing a substring
Replacing a substring of a C string is harder. In the case where the original and the replacement have the same number of chars, you can call strncpy() to copy over the characters.
// replacing a substring of equal length
char const *source = "foobar";

char *copy = strdup(source); // make a non-const copy of source

char *c2 = copy + 2;         // get pointer to char at index 2

strncpy(c2, "OBA", 3);       // copy 3 chars

NSLog(@"copy = %s", copy);
// prints "copy = foOBAr"

free(copy);                  // free copy when done
Replacing a substring with a different sized one is even more complex. There are three special cases that need to be handled: the substring to replace is at the start of the original, in the middle, or at the end. When the replacement substring is smaller than the original, there are some short cuts you can take to make the code a little simpler, but we'll only show the general case.

We'll look at the second case, replacing a substring in the middle of the original. With a little extra logic, this code can be adapted to handle all three of our cases.
char const *source = "The rain in Spain";
char const *original = "rain";     // substring to find
char const *replacement = "plane"; // substring to replace

// calculate the required buffer size
// including space for the null terminator
size_t size = strlen(source) - strlen(original) 
            + strlen(replacement) + sizeof(char);

// allocate buffer
char *buffer = calloc(size, sizeof(char));
if ( ! buffer) {
  // handle allocation failure
}

// find original substring in source and
// calculate the length of the unchanged prefix
char *originalInSource = strstr(source, original);
size_t prefixLength = originalInSource - source;

// copy prefix "The " into buffer
strncpy(buffer, source, prefixLength);

// calculate where the replacement substring goes in the buffer
char *replacementInBuffer = buffer + prefixLength;

// copy replacement "plane" into buffer
strcpy(replacementInBuffer, replacement);

// find position of unchanged suffix in source and
// calculate where it goes in the buffer
char const *suffixInSource = originalInSource + strlen(original);
char *suffixInBuffer = replacementInBuffer + strlen(replacement);

// copy suffix " in Spain" into buffer
strcpy(suffixInBuffer, suffixInSource);

NSLog(@"buffer = %s", buffer);
// prints "buffer = The plane in Spain"

free(buffer); // free buffer when done
I won't even waste your time explaining this in detail. No one programming in a modern computer language should have to write this code! It's extremely error prone and is one of the main causes of security vulnerabilities. If you find yourself doing this, stop immediately and seek out one of the many managed string libraries for C that are available. If you're writing code for iOS, you should be using NSString to do this.

Replacing using NSString
The NSString class has a number of useful methods for replacing characters and substrings in an NSString. Because NSString is immutable, these methods all return a new NSString instance containing the replacements, leaving the source NSString unchanged.

When you know the exact area of the string you want to replace, you can use the -stringByReplacingCharactersInRange:withString: method with an NSRange structure, which has fields for location (the zero-based index to start at) and length (the number of characters in the source string to replace). Because NSString does all the memory management for you and returns a new autoreleased NSString, it's child's play compared to doing this with C strings.
// replace a range in an NSString
NSString *source = @"The rain in Spain";
NSRange range;

range.location = 4; // starting index in source
range.length = 3;   // number of characters to replace in source

NSString *copy = [source stringByReplacingCharactersInRange:range 
                                                 withString:@"trai"];

NSLog(@"copy = %@", copy);
// prints "copy = The train in Spain"

// no need to release anything
// copy is autoreleased
This is a definite improvement over working with C strings. You might actually do this in real code without tearing your hair out or causing a buffer overrun bug. We can make this code even more compact by using the NSMakeRange() function to create the NSRange structure.
// replace a range in an NSString
NSString *source = @"The rain in Spain";

// create range in line
NSString *copy = [source stringByReplacingCharactersInRange:NSMakeRange(4, 3)
                                                 withString:@"trai"];

NSLog(@"copy = %@", copy);
// prints "copy = The train in Spain"

// no need to release anything
// copy is autoreleased
If you don't know ahead of time what part of the string you want to replace, you can do a find and replace in one method. The -stringByReplacingOccurrencesOfString:withString: method will find all occurrences of one NSString in another and replace them, returning a new autoreleased NSString.
// find and replace one substring with another
NSString *source = @"The rain in Spain";

NSString *copy = [source stringByReplacingOccurrencesOfString:@"ain"
                                                   withString:@"oof"];

NSLog(@"copy = %@", copy);
// prints "copy = The roof in Spoof"
There is another variation of this method that gives you more control over how substrings are found and replaced. The -stringByReplacingOccurrencesOfString:withString:options:range: method allows you to specify a mask containing one or more options and an NSRange structure allowing you to restrict the operation to a section of the string. The most common option is NSCaseInsensitiveSearch, which matches the substring without regard to case.
// case insensitive replace
NSString *source = @"<BR>The rain<BR>in Spain";

NSString *copy = [source stringByReplacingOccurrencesOfString:@"<br>"
                                                   withString:@"<p>"
                                                      options:NSCaseInsensitiveSearch
                                                        range:NSMakeRange(0, [source length])];

NSLog(@"copy = %@", copy);
// prints "copy = "<p>The rain<p>in Spain"
Another handy search option is NSAnchoredSearch, which searches only at the start of the source string. Notice that you use the bitwise or (|) operator to combine multiple options together.
// anchored, case insensitive replace
NSString *source = @"<BR>The rain<BR>in Spain";

NSString *copy = [source stringByReplacingOccurrencesOfString:@"<br>"
                                                   withString:@"<p>"
                                                      options:NSAnchoredSearch | NSCaseInsensitiveSearch
                                                        range:NSMakeRange(0, [source length])];

NSLog(@"copy = %@", copy);
// prints "copy = "<p>The rain<BR>in Spain"
You can combine the NSBackwardsSearch with NSAnchoredSearch to only replace the substring if it occurs at the end of the source instead of at the beginning.

Replacing in NSMutableString
If you're working with an NSMutableString, you can still call any of the -stringByReplacing... methods to produce a new NSString, but you have the option of making the replacements in the NSMutableString directly. The method -replaceCharactersInRange:withString: is very similar to the -stringByReplacingCharactersInRange:withString method:
// replace a range in an NSMutableString
NSMutableString *source = [NSMutableString stringWithString:@"The rain in Spain"];

[source replaceCharactersInRange:NSMakeRange(4, 3)
                      withString:@"trai"];

NSLog(@"source = %@", source);
// prints "source = The train in Spain"
The method -replaceOccurrencesOfString:withString:options:range: works similarly.

In most cases, there's not much of an advantage to replacing in place in an NSMutableString versus creating a new NSString containing the replacement. Use whichever operation is most convenient. If you need to make many replacements on a very long string, there may be an advantage to replacing in place rather than creating many large temporary NSString instances that live in the autorelease pool.

So far, the searching and replacing methods we've seen have done only simple string matching. Next week, we'll look at more powerful string matching using regular expressions.

Tuesday, September 28, 2010

Objective-C Tuesdays: searching in strings

Last week we looked at creating substrings of C strings and NSStrings. Today we look at another common string operation: searching within a string.

Find a character in a C string
As with all operations on C strings, searching requires you to deal with pointers. To find the first occurrence of a character in a C string, use the strchr() function. If the character is found, a pointer to that character is returned. If the character isn't present in the string, NULL is returned.
// find a character in a C string
char const *s = "foobar";

char const *character = strchr(s, 'b');
if (character) {
  NSLog(@"Found b");
} else {
  NSLog(@"Didn't find b");
}
// prints "Found b"
As we saw last week when we looked at substrings, the pointer returned by strchr() is effectively a substring of the source string starting at the first occurrence of the character you were searching for:
char const *s = "foobar";

char const *substring = strchr(s, 'b');
if (substring) {
  NSLog(@"The substring is %s", substring);
}
// prints "The substring is bar"
Once you find the character you're looking for, it's common to want to create a substring containing everything up to that position in the string:
char const *filename = "myfile.txt";

char const *dot = strchr(filename, '.');
if (dot) {
  size_t length = dot - filename;
  char *baseFilename = calloc(length + 1, sizeof(char));
  if (baseFilename) {
    strncpy(baseFilename, filename, length);
    NSLog(@"The base filename is %s");
  }
}
// prints "The base filename is myfile"
You use the difference between the two string pointers to calculate the number of chars up to (but not including) the character you searched for. After allocating a buffer to hold the new substring (and the null terminator), you use the strncpy() function to copy the first part of the source string. Because we called calloc(), the last char in our buffer is already set to zero; if you use malloc() or a fixed buffer instead, you need to remember to set the null terminator since strncpy() isn't guaranteed to do it for you.

Very often, you want to find the last occurrence of a character; you can use the strrchr() function to search in reverse:
// find a character in reverse
char const *filename = "myfile.txt";

char const *extension = strrchr(filename, '.');
if (extension) {
  NSLog(@"The extension is %s", extension);
}
// prints "The extension is .txt"

Find one C string in another
To find the first occurrence of one C string in another, use the strstr() function. Like strchr(), it returns a pointer to the first occurrence of the string, or NULL if it wasn't found.
// find one C string in another
char const *s1 = "The quick brown fox";

char const *s2 = strchr(s1, "ick");
if (s2) {
  NSLog(@"Found ick");
} else {
  NSLog(@"Didn't find ick");
}
// prints "Found ick"
Unfortunately the C standard library doesn't have a strrstr() function to search for the last occurrence of one string in another. You'll need to roll your own by calling strstr() in a loop until you reach the end of the string. (The implementation of this is left as an exercise for the reader, or better yet convert your C string to an NSString and keep reading :-)

C string encoding issues
The standard library functions for searching C strings work great with ASCII and similar single byte encodings. If you need to search inside UTF-8 encoded C strings, you'll quickly realize that strchr() and strrchr() are only useful for finding the basic ASCII characters (which are also valid UTF-8 characters). If you need to find non-ASCII characters like 'é', you'll need to use strstr() to search for the byte sequence that UTF-8 uses to represent it ("\xc3\xa9" for 'é'). Even then, Unicode characters like 'é' can be represented two ways: as the single Unicode character 'é' or as the base character 'e' followed by the combining character '´'. In general, it's better to use a C library designed to deal with the encoding such as the International Components for Unicode for handling UTF-8 encoded strings. Or if you're developing for iOS or Mac OS X, use NSString instead.

Find one NSString in another
The NSString class doesn't have separate methods to search for a single character or a string; you use -rangeOfString: to do either:
// find a character in an NSString
NSString *s = @"foobar";

NSRange range = [s rangeOfString:@"b"];
if (range.location != NSNotFound) {
  NSLog(@"Found b at %u", range.location);
}
// prints "Found b at 3"
Searching for the last occurrence of a string is done using the related method -rangeOfString:options: with the NSBackwardsSearch option.
// find last occurrence in an NSString
NSString *s = @"The rain in Spain falls mainly on the plain";

NSRange range = [s rangeOfString:@"ain" options:NSBackwardsSearch];
if (range.location != NSNotFound) {
  NSLog(@"Found ain at %u", range.location);
}
// prints "Found ain at 40"
The options are a combination of the following bit flags: NSCaseInsensitiveSearch, NSLiteralSearch, NSBackwardsSearch and NSAnchoredSearch. You use the bitwise or (|) operator to combine them together, or pass in zero for no options.

Use the NSCaseInsensitiveSearch option to find the first match, ignoring the case of both strings. The NSLiteralSearch option is used when you want to match a specific Unicode string form, such as the single character 'é' (Unicode character U+00E9) and not match equivalent character sequences like 'e' + '´' (Unicode characters U+0065 and U+0301). Most applications won't care about this option, but it's really handy when you need it.

NSAnchoredSearch checks for a match only at the start of the string (or the end if combined with NSBackwardsSearch). This option is occasionally handy, but the methods -hasPrefix: and -hasSuffix: are easier to read equivalents.
// anchored search
NSString *s = @"The rain in Spain falls mainly on the plain";

NSRange range = [s rangeOfString:@"ain" 
                         options:NSAnchoredSearch];
if (range.location == NSNotFound) {
  NSLog(@"Doesn't start with ain");
}
// prints "Doesn't start with ain"

// same thing using -hasPrefix:
if ( ! [s hasPrefix:@"ain"]) {
  NSLog(@"Doesn't have prefix ain");
}
// prints "Doesn't have prefix ain"


// now from the end
range = [s rangeOfString:@"ain"
                 options:NSAnchoredSearch | NSBackwardsSearch];
if (range.location != NSNotFound) {
  NSLog(@"Ends with ain");
}
// prints "Ends with ain"

// same thing using -hasSuffix:
if ([s hasSuffix:@"ain"]) {
  NSLog(@"Has suffix ain");
}
// prints "Has suffix ain"
There are two other variations of -rangeOfString:. The first, -rangeOfString:options:range:, allows you to search within a section of a larger string without having to create a substring.

The second, -rangeOfString:options:range:locale:, allows you to specify a locale as well as a range. In most cases you want to use the current locale, which is taken from the language setting on the user's device. The other variations of -rangeOfString: use the current locale, and you can pass nil for the locale to use the current one. Sometimes you know that the string contains text in a particular language, in an app that teaches German for instance. In this case you should specify a locale when searching the string; the locale can affect how text is matched, especially when using the NSCaseInsensitiveSearch option.

Next week, we'll look at replacing characters in C strings and NSStrings.