Tuesday, November 17, 2009

Objective-C Tuesdays: break out of a loop

Last week we finished looking at the four looping statements in Objective-C: the for loop, the Objective-C 2.0 for...in loop, the while loop and the do...while loop. Today we'll look at how you break out of a loop early.

Sometimes you want to stop a loop prematurely. A common example of this is when you need to iterate over an unsorted collection or array looking for an item:
NSArray *collection = [NSArray arrayWithObjects:@"foo", @"a", @"bar", @"baz", @"b", nil];
for (NSUInteger i = 0; i < collection.count; i++) {
  NSString *item = [collection objectAtIndex:i];
  NSLog(@"Checking item '%@'", item);
  if (item.length == 1) {
    NSLog(@"Found single letter at index %u", i);
    /* no need to look at the rest -- we can stop now */
  }
}
Once you find the item you're looking for, there's no need to finish the loop. In fact, if you search the whole collection, you will always find the last item that matches your criteria, which isn't always what you want.

You can use a boolean flag variable to indicate when you're done:
NSArray *collection = [NSArray arrayWithObjects:@"foo", @"a", @"bar", @"baz", @"b", nil];
BOOL foundFirst = NO;
for (NSUInteger i = 0; i < collection.count && ! foundFirst; i++) {
  NSString *item = [collection objectAtIndex:i];
  NSLog(@"Checking item '%@'", item);
  if (item.length == 1) {
    NSLog(@"Found first single letter at index %u", i);
    foundFirst = YES;
  }
}
This approach works, but is somewhat complicated and error-prone. A nicer way to exit a loop early is to use the break keyword. Using break, the example becomes:
NSArray *collection = [NSArray arrayWithObjects:@"foo", @"a", @"bar", @"baz", @"b", nil];
for (NSUInteger i = 0; i < collection.count; i++) {
  NSString *item = [collection objectAtIndex:i];
  NSLog(@"Checking item '%@'", item);
  if (item.length == 1) {
    NSLog(@"Found first single letter at index %u", i);
    break;
  }
}
When the break statement is encountered, loop processing is stopped and execution resumes with the first statement after the loop; you can think of break as a sort of return statement for loops.

You can use break to exit any of the loop statements. If we don't care about the index of the first item, we can simplify the example further by using the for...in loop:
NSArray *collection = [NSArray arrayWithObjects:@"foo", @"", @"bar", @"baz", @"", nil];
for (NSString *item in collection) {
  NSLog(@"Checking item '%@'", item);
  if (item.length == 1) {
    NSLog(@"Found first single letter");
    break;
  }
}
The break statement only works on the innermost loop that encloses it:
// outer loop
for (int i = 0; i < 10; i++) {
  if (...) break; // skips to A
  
  // inner loop
  for (int j = 0; j < 10; j++) {
    if (...) break; // skips to B
  }
  // B
  
}
// A
The first break statement is inside the outer loop, and will skip to A. The second break statement is inside the inner loop; it skips to B, ending the inner loop but staying inside the outer loop.

Next week we will look at the other loop flow modifier, continue statement.

No comments: