See Also
Looping in Objective-C
Last week we looked at how to end a loop early using the Looping in Objective-C
break
keyword. Today we will look at a similar action: how to skip to the next iteration.Sometimes you need to process each item in a collection or sequence, but some of those items get full processing and some don't. For example, you may want to skip empty strings in a collection. Frequently you do this using an
if...else
statement:NSArray *collection = [NSArray arrayWithObjects: @"foo", @"", @"bar", @"baz", @"", nil]; int wordCount = 0; for (NSString *item in collection) { NSLog(@"found '%@'", item); if (item.length > 0) { wordCount++; } } NSLog(@"word count = %d", wordCount);This is generally a good approach, but sometimes you have complex nested logic:
NSArray *collection = [NSArray arrayWithObjects: @"foo", @"\n", @"bar", @"baz", @"", nil]; int wordCount = 0; for (NSString *item in collection) { NSLog(@"found '%@'", item); if (item.length > 0) { if ( ! [item isEqualToString:@"\n"]) { wordCount++; if (item.length < 4) { NSLog(@"short word"); } else { NSLog(@"long word"); } } } } NSLog(@"word count = %d", wordCount);The
continue
statement can help you simplify complicated cases like this by stopping the execution of the loop body for the current item and advancing to the next. Using continue
, we can rewrite the example like this:NSArray *collection = [NSArray arrayWithObjects: @"foo", @"\n", @"bar", @"baz", @"", nil]; int wordCount = 0; for (NSString *item in collection) { NSLog(@"found '%@'", item); if (item.length > 0) continue; if ([@item isEqualToString:"\n"]) continue; wordCount++; if (item.length < 4) { NSLog(@"short word"); } else { NSLog(@"long word"); } } NSLog(@"word count = %d", wordCount);Like
break
, a continue
statement only works on the innermost loop that encloses it:// outer loop for (int i = 0; i < 10; i++) { // loop A if (...) continue; // skips to next item in A // inner loop for (int j = 0; j < 10; j++) { // loop B if (...) continue; // skips to next item in B } if (...) continue; // skips to next item in A }The
continue
statement is most useful with a for
or for...in
loop, but can be used with a while
and do...while
loop with care. It's easy to create an infinite while
loop using continue
:NSArray *collection = [NSArray arrayWithObjects: @"foo", @"", @"bar", @"baz", @"", nil]; int wordCount = 0; int i = 0; while (i < collection.count) { NSString *item = [collection objectAtIndex:i]; NSLog(@"found '%@'", item); if (item.length > 0) { continue; // OOPS! forgot to increment i } wordCount++; i++; } NSLog(@"word count = %d", wordCount);This loop will reach the second item in the collection and get stuck there -- it never reaches the
i++
at the end of the loop body. The solution is simple:NSArray *collection = [NSArray arrayWithObjects: @"foo", @"", @"bar", @"baz", @"", nil]; int wordCount = 0; int i = 0; while (i < collection.count) { NSString *item = [collection objectAtIndex:i]; NSLog(@"found '%@'", item); if (item.length > 0) { i++;; // move to next item continue; } wordCount++; i++; } NSLog(@"word count = %d", wordCount);This is a consequence of the free-form nature of the
while
and do...while
loops. The compiler knows how to make a for
or for...in
loop advance to the next item, but the other loops leave that up to you; continue
acts more like a special goto
statement with while
and do...while
loops.Next time, we'll look at the mother of all loops, the
goto
statement.
3 comments:
I believe your 2nd example code block in this post has two errors, 1 logic and 1 syntax:
If (item.length > 0 ) continue;
// should be
if (item.length == 0) continue;
// and
if ([@item isEqualToString:"\n"]) continue;
// should be
if ([item isEqualToString:@"\n"]) continue;
Thanks Dave! I fixed the syntax error in the second example, the @ is now with the "\n" where it belongs. The if statement now reads:
if ( ! [item isEqualToString:@"\n"]) { ...
I think the first if statement should still be:
if (item.length > 0) { ...
The purpose of the if statement is to exclude empty strings. It could alternately be written as:
if ( ! [item isEqualToString:@""]) { ...
I hope you enjoyed the post otherwise.
Great article! I believe, however, that what Dave was referring to by:
If (item.length > 0 ) continue;
// should be
if (item.length == 0) continue;
was in your 3rd code block. As it is, an item of length greater than 0 would skip (continue) the rest of the loop, when you really want the opposite effect.
Post a Comment