Flow Control and Conditions
Basic flow control is fairly simple and usually involves a condition in parentheses and a block of conditionally executed code in curly braces. These curly braces constitute a new scope, into which new variables can be introduced. So, for example:
if (x == 7) { int i = 0; i += 1; }
After the closing curly brace in the fourth line, the i
introduced in the second line has ceased to exist, because its scope is the inside of the curly braces. If the contents of the curly braces consist of a single statement, the curly braces can be omitted, but I would advise beginners against this shorthand, as you can confuse yourself. A common beginner mistake (which will be caught by the compiler) is forgetting the parentheses around the condition. The full set of flow control statements is given in K&R Chapter 3, and I’ll just summarize them schematically here (Example 1-1).
if (condition) { statements; } if (condition) { statements; } else { statements; } if (condition) { statements; } else if (condition) { statements; } else { statements; } while (condition) { statements; } do { statements; } while (condition); for (before-all; condition; after-each) { statements; }
The if...else if...else
structure can have as many else if
blocks as needed, and the else
block is optional. Instead of an extended if...else if...else if...else
structure, when the conditions would consist of comparing various values against a single value, you can use the switch
statement; be careful, though, as it is rather confusing and can easily go wrong (see K&R 3.4 for full details). The main trick is to remember to end every case with a break
statement, unless you want it to “fall through” to the next case (Example 1-2).
NSString* key; switch (tag) { case 1: { // i.e., if tag is 1 key = @"lesson"; break; } case 2: { // i.e., if tag is 2 key = @"lessonSection"; break; } case 3: { // i.e., if tag is 3 key = @"lessonSectionPartFirstWord"; break; } }
The C for loop needs some elaboration for beginners (Example 1-1). The before-all
statement is executed once as the for loop is first encountered and is usually used for initialization of the counter. The condition is then tested, and if true, the block is executed; the condition is usually used to test whether the counter has reached its limit. The after-each
statement is then executed, and is usually used to increment or decrement the counter; the condition is then immediately tested again. Thus, to execute a block using integer values 1, 2, 3, 4, and 5 for i
, the notation is:
int i; for (i = 1; i < 6; i++) { // ... statements ... }
The need for a counter intended to exist solely within the for loop is so common that C99 permits the declaration of the counter as part of the before-all
statement; the declared variable’s scope is then inside the curly braces:
for (int i = 1; i < 6; i++) { // ... statements ... }
The for loop is one of the few areas in which Objective-C extends C’s flow-control syntax. Certain Objective-C objects represent enumerable collections of other objects; “enumerable” basically means that you can cycle through the collection, and cycling through a collection is called enumerating the collection. To make enumerating easy, Objective-C provides a for...in
operator, which works like a for loop:
SomeType* oneItem; for (oneItem in myCollection) { // ... statements .... }
On each pass through the loop, the variable oneItem
(or whatever you call it) takes on the next value from within the collection. As with the C99 for loop, oneItem
can be declared in the for
statement, limiting its scope to the curly braces:
for (SomeType* oneItem in myCollection) { // ... statements .... }
To abort a loop from inside the curly braces, use the break
statement. To abort the current iteration from within the curly braces and proceed to the next iteration, use the continue
statement. In the case of while
and do
, continue
means to perform immediately the conditional test; in the case of a for loop, continue
means to perform immediately the after-each
statement and then the conditional test.
C also has a goto
statement that allows you to jump to a named (labeled) line in your code (K&R 3.8); even though goto
is notoriously “considered harmful,” there are situations in which it is pretty much necessary, especially because C’s flow control is otherwise so primitive.
Note
It is permissible for a C statement to be compounded of multiple statements, separated by commas, to be executed sequentially. The last of the multiple statements is the value of the compound statement as a whole. This construct, for instance, lets you perform some secondary action before each test of a condition or perform more than one after-each
action (an example appears in Chapter 17).
We can now turn to the question of what a condition consists of. C has no separate boolean type; a condition either evaluates to 0, in which case it is considered false, or it doesn’t, in which case it is true. Comparisons are performed using the equality and relational operators (K&R 2.6); for example, ==
compares for equality, and <
compares for whether the first operand is less than the second. Logical expressions can be combined using the logical-and operator (&&
) and the logical-or operator (||
); using these along with parentheses and the not operator (!
) you can form complex conditions. Evaluation of logical-and and logical-or expressions is short-circuited, meaning that if the left condition settles the question, the right condition is never even evaluated.
Warning
Don’t confuse the logical-and operator (&&
) and the logical-or operator (||
) with the bitwise-and operator (&
) and the bitwise-or operator (|
) discussed earlier. Writing &
when you mean &&
(or vice versa) can result in surprising behavior.
The operator for testing basic equality, ==
, is not a simple equal sign; forgetting the difference is a common novice mistake. The problem is that such code is legal: simple assignment, which is what the equal sign means, has a value, and any value is legal in a condition. So consider this piece of (nonsense) code:
int i = 0; while (i = 1) { i = 0; }
You might think that the while condition tests whether i
is 1. You might then think: i
is 0, so the while body will never be performed. Right? Wrong. The while condition does not test whether i
is 1; it assigns 1 to i
. The value of that assignment is also 1, so the condition evaluates to 1, which means true. So the while body is performed. Moreover, even though the while body assigns 0 to i
, the condition is then evaluated again and assigns 1 to i
a second time, which means true yet again. And so on, forever; we’ve written an endless loop, and the program will hang. (And, depending on what compiler and settings you’re using, you might not even get a warning of trouble ahead.)
C programmers actually revel in the fact that testing for zero and testing for false are the same thing and use it to create compact conditional expressions, which are considered elegant and idiomatic. I don’t recommend that you make use of such idioms, as they can be confusing, but I must admit that even I do occasionally resort to it, especially because in Objective-C it is so common to test an object reference to see whether it is nil (discussed further in Chapter 3). Since nil is a form of zero, I usually ask whether an object s
is nil like this:
if (s) { // ... }
Objective-C introduces a BOOL type, which you should use if you need to capture or maintain a condition’s value as a variable, along with constants YES and NO (actually representing 1 and 0), which you should use when setting a boolean value. Don’t compare anything against a BOOL, not even YES or NO, because a value like 2 is true in a condition but is not equal to YES or NO. Just use the BOOL directly as a condition, or as part of a complex condition, and all will be well. For example:
BOOL isnil = (nil == s); if (isnil) { // not: if (isnil == YES) // ... }
Get Programming iOS 6, 3rd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.