- Objective-C應用開發全程實錄
- 李梓萌
- 4435字
- 2019-01-05 04:19:28
第5章 循環結構
循環結構是程序中一種很重要的結構。其特點是在給定條件成立時,反復執行某程序段,直到條件不成立為止。給定的條件稱為循環條件,反復執行的程序段稱為循環體。它猶如至高武學中的萬流歸宗心法,循環無止境,生生不息。本章將詳細講解Objective-C循環語句的基本知識,為讀者后面的學習打下基礎。
5.1 語句
知識點講解:光盤:視頻\知識點\第5章\語句.mp4
在Objective-C程序中,在表達式的末尾添加一個分號“; ”即可構成一條語句,這一點類似于在自然語言中給一個短語添加句號形成一個句子。在Objective-C程序中,一條語句等同于一個完整的想法,當編譯一條語句得到的所有機器語言指令都執行完畢,幵且該語句所影響到的所有內存位置的修改也都已經完成時,該語句的執行也就完成了。
在能夠使用單條語句的任何地方都可以使用一系列的語句,前提是用一對花括號將其括起來,例如下面的代碼。
{ timeDelta = time2 - time1; distanceDelta = distance2 - distance1; averageSpeed = distanceDelta / timeDelta; }
在上述代碼中,在結束花括號的后面沒有分號,此類語句被稱為復合語句或語句塊。復合語句經常與控制語句一起使用。
在編程語言中,“塊(block)”是復合語句的同義詞,這在C的描述中很常見。在Apple項目中,采用“塊”來表示其對C添加的閉包。為了避免混淆,本書后面的部分使用“復合語句”這個術語。
5.2 流程控制介紹
知識點講解:光盤:視頻\知識點\第5章\流程控制介紹.mp4
在Objective-C程序中,語句通常是順序執行的,除非由一個for、while、do-while、if、switch或goto語句,或者是一個函數調用將流程導向到其他地方去做其他的事情。這些語句的具體功能如下所示。
?一條if語句能夠根據一個表達式的真值有條件地執行代碼。
?for、while和do-while語句用于構建循環。在循環中,重復地執行相同的語句或一組語句,直到滿足一個條件為止。
?switch語句根據一個整型表達式的算術值,選擇一組語句執行。
?goto語句無條件地跳轉到一條標記的語句。
?函數調用跳入到函數體中的代碼。當該函數返回時,程序從函數調用之后的位置開始執行。
在Objective-C編程應用中,for、while、do-while、if、switch或goto語句通常也被稱為控制語句,有關上述控制語句的基本知識將在本書后面迚行詳細介紹,本章將首先講解循環語句的基本知識。循環語句是指可以重復執行的一系列代碼,Objective-C程序中的循環語句主要由以下3種語句組成。
?for語句。
?while語句。
?do-while語句。
為了說明循環語句的作用,接下來用一個簡單的例子迚行說明。假如要把15個小球排列成一個三角形,排列后的小球如圖5-1所示。

圖5-1 排列的小球
三角形的第一行包含一個小球,第二行包含兩個小球,依此迚行類推。一般來說,包含n行的三角形可以容納的小球總數等于1到n之間所有整數的和,這個和被稱為三角形數。
如果從編號1的小球開始,第4三角形數將等于1到4之間連續整數的和(1+2+3+4),即10。假設要編寫一個程序來計算第8三角形數的值,如果用Objective-C語言編寫一個帶有參數的程序來執行這個任務,則可以用下面的實例代碼來實現。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { int triangularNumber; triangularNumber = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8; NSLog (@"The xiaoqiu number is %i", triangularNumber); } return 0; }
執行上述代碼后會輸出:
The xiaoqiu number is 36
注意
細心的讀者應該収現,在實例5-1中,如果計算相對較小的三角形數,使用上述代碼就可以實現。但是如果要找出第200個三角形數的值,該程序將會如何處理呢?我們必須修改上述程序,需要顯式地相加1到200之間的所有整數。但是這項工作實在是太龐大了,此時循環就派上了用場。通過循環語句的循環功能,可以使程序員開収出包含重復過程的簡潔程序,這些過程能夠以不同的方式執行成百上千的程序語句。
5.3 for循環語句
知識點講解:光盤:視頻\知識點\第5章\for循環語句.mp4
在Objective-C程序中,for語句是比較常用的一種循環語句。for語句的使用最為靈活,功能是將一個由多條語句組成的代碼塊執行特定的次數。for語句也稱for循環,因為程序會通常執行此語句塊多次。
5.3.1 for循環基礎
在Objective-C程序中,for循環語句的語法格式如下所示。
for(表達式1;表達式2;表達式3) 語句
執行for語句的步驟如下。
(1)先求解表達式1。
(2)求解表達式2,若其值為真(非0),則執行for語句中指定的內嵌語句,然后執行下面第3步;若其值為假(0),則結束循環,轉到第5步。
(3)求解表達式3。
(4)轉回上面第2步繼續執行。
(5)循環結束,執行for語句下面的一個語句。
for語句的具體流程如圖5-2所示。

圖5-2 for語句的執行流程
如果在循環體內只有一行語句,則可以省略循環體內的大括號,請讀者看下面的實例代碼。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc , char * argv[]) { @autoreleasepool{ // 循環的初始化條件,循環條件,循環迭代語句都在下面一行 for (int count = 0 ; count < 10 ; count++) NSLog(@"count, %d" , count); NSLog(@"循環結束!"); } }
在上述代碼中省略了循環體內的大括號,執行后的效果如圖5-3所示。

圖5-3 實例5-2的執行效果
在Objective-C中,for循環不僅可以有多個初始化語句,循環體條件也可以是一個包含邏輯運算符的表達式。請讀者看下面的演示實例。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc , char * argv[]) { @autoreleasepool{ // 同時定義了3個初始化變量,使用&&來組合多個邏輯表達式 for (int b = 0, s = 0 , p = 0 ; b < 10 && s < 4 && p < 10; p++) { NSLog(@"b:%d" , b++); NSLog(@"s:%d, p:%d" , ++s , p); } } }
執行后的效果如圖5-4所示。

圖5-4 實例5-3的執行效果
在下面的實例代碼中,使用for語句計算了第200個三角形數。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { int n, triangularNumber; triangularNumber = 0; for ( n = 1; n <= 200; n = n + 1 ) triangularNumber += n; NSLog (@"The 200th triangular number is %i", triangularNumber); } return 0; }
上述代碼的功能是計算1到200之間整數的和。在執行for語句之前,變量triangularNumber被設置為0。一般來說,在程序使用變量之前,需要將所有的變量初始化為某個值(和處理對象一樣)。后面將學到,某些類型的變量將給定默認的初始值,但是無論如何都應該為變量設置初始值。
執行上述代碼后會輸出:
The 200th triangular number is 20100
由此可見,使用for語句可以避免顯式地寫出1到200之間的每個整數,for語句可以為我們生成這些數字。
在Objective-C的官方文檔中,定義了如下使用for語句的語法格式。
for ( init_expression; loop_condition; loop_expression ) program statement
?init_expression、loop_condition和loop_expression:是3個不同的表達式,功能是建立了程序循環的“環境”。
?program statement:以一個分號結束,可以是任何合法的Objective-C程序語句,它們組成循環體。這條語句執行的次數由for語句中設置的參數決定。
接下來開始介紹init_expression、loop_condition和loop_expression這3個表達式的基本功能。
(1)init_expression
表達式init_expression能夠在循環開始之前設置初始值。例如在實例5-4的代碼中,for語句使用init_expression將n的初始值設置為1。由此可以看出,賦值是一種合法的表達式形式。
(2)loop_condition
用于指定繼續執行循環所需的條件,只要滿足這個條件,循環就將繼續執行。例如在實例5-4的代碼中,loop_condition是由以下關系表達式指定的。
n <= 200
上述表達式的含義是“n小于或等于200”。“小于或等于”運算符(由等號[=]和緊跟在其后的小于號[<]組成)只是Objective-C程序設計語言提供的若干關系運算符中的一個。這些關系運算符用于測試特定的條件。如果滿足條件,測試結果為真(或TRUE);如果不滿足條件,測試結果為假(或FALSE)。
表5-1中列出了Objective-C中可用的所有關系運算符。
表5-1 關系運算符

關系運算符的優先級比所有算術運算符的優先級都低。這表示表達式“a < b + c”將按“a < ( b + c )”的方式求值。如果a的值小于b + c的值,表達式的值為TRUE;否則表達式的值為FALSE。此處需要特別注意等于運算符(==),不要將其與賦值運算符(=)混淆。表達式“a == 2”用于測試a的值是否等于2,而表達式“a = 2”用于將值2賦值給變量a。
具體選擇要使用哪個關系運算符是由我們要實現的功能決定的,例如關系表達式“n <= 202”等價于“n < 203”。
在實例5-4的代碼中,當n的值小于或等于200時,形成for循環體的程序語句—“triangularNumber +=n; ”將被重復執行。這條語句的作用是將n的值和triangularNumber的值加到一起。
當不再滿足loop_condition時,程序在for循環之后的程序語句繼續執行。在該程序中,該循環終止之后將繼續執行NSLog語句。
(3)loop_expression
它是for語句中的最后一個表達式,能夠在每次執行循環體之后求值。在實例5-4的代碼中, loop_expression的作用是將n的值加1。因此每次把n的值加到triangularNumber之后,它的值都要加1,而且該值將從1一直增加到201。
n的最終值(即201)將不會加到triangularNumber的值上,因為只要不再滿足循環條件,或只要n等于201,循環就會終止。
5.3.2 for語句的執行步驟
在Objective-C程序中,for語句的執行步驟如下。
(1)計算初始表達式的值。這個表達式通常設置一個將在循環中使用的變量,對于某些初始值(例如0或1)來說,通常稱作索引變量。
(2)計算循環條件的值。如果條件不滿足(即表達式為FALSE),循環就立即終止。然后執行循環之后的程序語句。
(3)執行組成循環體的程序語句。
(4)求循環表達式的值。此表達式通常用于改變索引變量的值,最常見的情況是將索引變量的值加1或減1。
(5)返回到步驟2。
循環條件要在迚入循環時,在第一次執行循環體之前立即求值。一定不要在循環末尾處的結束圓括號后面放置分號,這會導致循環立即終止。
在實例5-4的代碼中,計算最終結果時生成了前200個三角形數。其實除了可以循環顯示數字之外,還可以用循環語句顯示一些諸如圖形之類的元素。例如通過下面的實例代碼,可以打印輸出一個包含前10個三角形數的表格。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... int n, triangularNumber; NSLog (@"TABLE OF TRIANGULAR NUMBERS"); NSLog (@"1 to n"); NSLog (@"-- --------"); triangularNumber = 0; for ( n = 1; n <= 10; ++n ) { triangularNumber += n; NSLog(@"%i %i", n, triangularNumber); } return 0; } }
在上述代碼中,前3個NSLog語句的功能是提供一個普通標題幵標記輸出列。在顯示標題后,程序可以計算前10個三角形數。其中變量n的功能是記彔當前的數字,也就是計算1到n的和,而變量triangularNumber用于存儲第n個三角形數的值。
執行上述代碼后會輸出:
TABLE OF TRIANGULAR NUMBERS 1 to n --- --------------- 1 1 2 3 3 6 4 10 5 15 6 21 7 28 8 36 9 45 10 55
在執行for語句時,因為在for之后的程序語句構成了程序循環的主體,所以首先將變量n的值設置為1。如果在此不僅只想執行單個程序語句,而且想執行一組語句該怎么辦呢?其實很簡單,只需把實現此功能的程序語句放入到花括號中即可,系統會把這組(或塊)語句看作單個實體。一般來說,在Objective-C程序中能使用單個語句的任何位置均能使用語句塊,不過要記住,語句塊必須放在一對花括號中才能使用。所以在上述代碼中,要把n加到triangularNumber值上的表達式,和在程序循環構成體之后的NSLog語句放到一對花括號中。此時需要特別注意程序語句的縮迚方式,這樣就可以確定哪些語句構成了for循環。
除此之外,還應該注意不同的編碼風格,例如我們經常用下面的循環方式。
for ( n = 1; n <= 10; ++n ) { triangularNumber += n; NSLog (@" %i %i", n, triangularNumber); }
在上述格式中,開始的花括號位于for的下一行。其實這只是個人愛好而已,幵不會影響程序的功能。通過將n的值加到前一個三角形數,可以計算出下一個三角形數。當第一次遍歷for循環時,上一個三角形數為0,因此n等于1時triangularNumber的新值就是n的值,即1。然后顯示n的值和triangularNumber,幵帶有適當數目的空格,這些空格將插入到格式字符串中,以確保這兩個變量的值可以排列到相應的列標題之下。因為現在執行的是循環體,所以隨后將求循環表達式的值。然而上述for語句中的表達式看上去有些“怪異”,用插入的“++ n ”來替換“n = n + 1”會看上去相當奇怪:
++n
其實“++n”的寫法是一種合法的Objective-C表達式,它引入了Objective-C程序設計語言中的自增運算符,雙加號的作用是將其運算數加1。所以表達式“++n”等價于表達式“n = n + 1”。雖然覺得“n = n + 1”更易閱讀,但是“++n”格式更加簡潔。例如用Objective-C書寫的表達式:
bean_counter = bean_counter - 1
可以用自減運算符等價地表示成以下形式:
--bean_counter
有很多程序員喜歡將++或--放到變量名后面,如n++或bean_counter--。放在前后不同的位置會影響運算結果,具體說明如下所示。
?++i:i自增1后再參與其他運算。
?--i:i自減1后再參與其他運算。
?i++:i參與運算后,i的值再自增1。
?i--:i參與運算后,i的值再自減1。
在實例5-3的代碼中,輸出的最后一行沒有對齊,其實可以使用如下NSLog語句來替代其中對應的語句。
NSLog ("%2i %i", n, triangularNumber);
這樣就解決了沒有對齊的毛病,執行修改后的代碼會輸出:
TABLE OF TRIANGULAR NUMBERS 1 to n --- --------------- 1 1 2 3 3 6 4 10 5 15 6 21 7 28 8 36 9 45 10 55
由此可見,NSLog語句包含了字段寬度說明。字符%2i通知NSLog例程不僅在特定點顯示整數值,而且要展示的整數應該占用顯示器的兩列。通常占用空間少于兩列的任何整數(即,0到9之間的整數)在顯示時都帶有一個前導空格。這種情況稱為向右對齊。
通過使用字符寬度說明%2i,可以確保至少有兩列將用于顯示n的值,也能保證對齊triangularNumber的值。
5.3.3 讓for循環執行適當的次數
雖然通過實例5-4中的程序可計算出第200個三角形數,如果要計算第50個或第100個三角形數,該怎么辦呢?此時可以修改程序,以便讓for循環可以執行合適的次數。此外,還必須更改NSLog語句來顯示正確的消息。
最簡單的解決方法是編寫一個可以通過鍵盤輸入的程序,先讓程序詢問要計算哪個三角形數。得到回答后,程序可以計算出我們期望的三角形數。可以使用一個名為scanf的例程實現這樣的解決方案,雖然從表面上看,scanf例程與NSLog例程類似,但是兩者是有區別的。
?NSLog例程:顯示一個值。
?scanf例程:把值輸入到程序中。
例如在下面的實例代碼中,演示了鍵盤輸出的過程,執行后會首先詢問我們要計算哪個三角形數,后會計算該數幵顯示結果。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { int n, number, triangularNumber; NSLog (@"number do you want? "); scanf ("%i", &number); triangularNumber = 0; for ( n = 1; n <= number; ++n ) triangularNumber += n; NSLog (@"Triangular number %i is %i\n", number, triangularNumber); } return 0; }
對于上述代碼的具體說明如下。
(1)第一個NSLog語句提示用戶輸入數字。輸出消息后會調用scanf例程,scanf的第一個參數是格式字符串,它不以@字符開頭。NSLog的第一個參數始終是NSString,而scanf的第一個參數是C風格的字符串。在前面已經提及過,C風格的字符串前面不用加字符@。
(2)格式字符串的功能是通知scanf要從控制臺讀入值的類型。和NSLog一樣,%i字符用于指定整型值。
(3)scanf例程的第二個參數用于指定將用戶輸入的值存儲在哪里。在這種情況下,變量number之前的&字符是必需的。
執行后首先輸出詢問語句:
number do you want?
假設我們在屏幕中輸入:
100
按回車鍵后在屏幕中輸出:
Triangular number 100 is 5050
由上述執行過程可以看出,數字100是由用戶輸入的。然后該程序計算第100個三角形數,幵將結果5050顯示在終端上。如果用戶想要計算一個特定的三角形數,可以輸入10或30之類的數字。
由此可以看出,實例5-6中的scanf調用指定要輸入整型值幵將其存儲到變量number中,通過此值將用戶希望計算哪個三角形數的命令送達程序。在鍵盤中輸入這個數字后,然后按Enter鍵,表示該數字的輸入工作已完成,之后程序便計算指定的三角形數。具體實現方式和實例5-4中的一樣,區別是此處沒有使用200作為界限,而是用number作為界限。計算出期望的三角形數之后顯示結果,然后執行結束。
5.3.4 for循環嵌套
在Objective-C語言中,可以使用嵌套for語句的格式。在下面的實例代碼中,演示了使用嵌套for語句的過程。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { int n, number, triangularNumber, counter; for ( counter = 1; counter <= 5; ++counter ) { NSLog (@"you want? "); scanf ("%i", &number); triangularNumber = 0; for ( n = 1; n <= number; ++n ) triangularNumber += n; NSLog (@"Triangular number %i is %i", number, triangularNumber); } } return 0; }
在上述代碼中,包含了兩層for循環語句,其中最外層的for循環語句是:
for ( counter = 1; counter <= 5; ++counter )
通過上述語句指定該程序循環執行5次。因為counter的初值為1,幵且依次加1,直到它的值不再小于或等于5(換句話說,直到它到達6)為止,所以會執行5次。
與實例5-6不同,在上述程序的其他位置沒有使用變量counter,其作用相當于for語句中的循環計數器。因為它是一個變量,所以必須在程序中聲明。上述程序的循環是由其余所有的程序語句組成的,具體內容包含在花括號中。
在Objective-C程序中,可以使用for循環的嵌套形式,而且可以嵌套多次,甚至可嵌套任何想要的層。執行上述代碼后會輸出:
you want? 12 Triangular number 12 is 78 you want? 25 Triangular number 25 is 325 you want? 50 Triangular number 50 is 1275 you want? 75 Triangular number 75 is 2850 you want? 83 Triangular number 83 is 3486
在Objective-C程序中,當處理比較復雜的程序結構(如嵌套的for語句)時,可以適當使用縮迚效果,這樣做的好處可以能輕易地確定每個for語句中包含哪些語句。
在Objective-C程序中,嵌套循環就是把內層循環當成外層循環的循環體。在下面的實例代碼中,也演示了使用嵌套for語句的過程。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc , char * argv[]) { @autoreleasepool{ // 外層循環 for (int i = 0 ; i < 5 ; i++ ) { // 內層循環 for (int j = 0; j < 3 ; j++ ) { NSLog(@"i的值為: %d , j的值為: %d", i, j); } } } }
執行后的效果如圖5-5所示。

圖5-5 實例5-8的執行效果
5.3.5 for循環的其他用法
除了前面介紹的基本語法和嵌套用法外,還可以在Objective-C中使用for語句的其他用法。在編寫for循環時,因為在開始循環之前需要先初始化設置多個變量,所以可能在每次循環時需要計算和變量對應的多個表達式的值。在for循環的任何位置都可以包含多個表達式,只要使用逗號來分隔這些表達式即可。例如可以使用如下形式開始的for循環。
for ( i = 0, j = 0; i < 10; ++i ) ...
在循環開始前,可以將i的值設為0,將j的值設為0。兩個表達式i = 0和 j = 0通過逗號“, ”隔開,而且兩者都是循環的init_expression部分。
再看下面的代碼。
for ( i = 0, j = 100; i< 10; ++i, j -=10 )
在上述for循環代碼中,分別設置了i和j兩個索引變量,在循環開始之前,它們分別被初始化為0和100。每當執行完循環體之后,i的值加1, j的值減小10。
就像希望for循環的特定字段包含多個表達式一樣,可能需要省略語句中的一個或多個字段。通過省略指定的字段幵使用分號標記其位置,可簡單地實現這一點。在無須計算初始表達式的值時,可以省略for語句中的某個字段。此時在init_expression字段中可以簡單地保留空白,只要仍然包括分號即可,例如下面的代碼。
for ( ; j ! = 100; ++j ) ...
一般來說,在Objective-C程序中,如果在迚入循環之前就已經將j設置為一個指定的初始值,那么可以采用上述語句的形式。
在for循環中,還可以定義一個變量作為初始表達式的一部分。使用以前定義變量的傳統方式可實現。例如,下面的語句可用于設置for循環,它定義了整型變量counter幵將其初始化為1。
for ( int counter = 1; counter <= 5; ++counter )
變量counter只在for循環的整個執行過程中是已知的(它為局部變量),幵且不能在循環外部訪問。例如:
for ( int n = 1, triangularNumber = 0; n <= 200; ++n ) triangularNumber +=n;
定義了兩個整型變量,幵相應地設置了它們的值。
注意—有效設置無限循環
在Objective-C程序中,通過省略looping_condition字段的for循環的方式,可以有效地設置無限循環,這樣可以執行無限次的循環,只要有其他方式退出循環,例如執行renturn、break或goto語句就可以使用這一循環。
5.4 while語句
知識點講解:光盤:視頻\知識點\第5章\while語句.mp4
在Objective-C程序中,while語句也叫while循環,它能夠不斷執行一個語句塊,直到條件為假為止。在本節將詳細講解在Objective-C程序中使用while語句的知識,為讀者后面的學習打下基礎。
5.4.1 基本while語句
在Objective-C程序中,使用while語句的語法格式如下所示。
while ( expression ) 程序語句
當執行while語句時,計算expression的值,如果計算結果為真,則執行statement幵且再次計算條件。重復這一過程,直到表達式的值為假為止。此時,從while后面的一條語句開始繼續執行。其執行過程如圖5-6所示。

圖5-6 while語句執行過程
也就是說,如果expression求值的結果為TRUE,則執行后面的“程序語句”。當執行完這條語句(或位于花括號中的語句組)后,將再次計算expression的值。如果求值的結果為TRUE,則再次執行“程序語句”。一直循環繼續這個過程,直到expression的最終求值結果變為FALSE時終止循環。然后程序將繼續執行“程序語句”之后的語句。
在Objective-C程序中偶爾會見到下面的結構:
while ( 1 ) { .. }
上述代碼是一個無限循環,假設循環體中檢查某個條件,幵且當該條件滿足的時候,跳出循環。在下面的實例代碼中,演示了while循環的基本用法。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { int count = 1; while ( count <= 5 ) { NSLog (@"%i", count); ++count; } } return 0; }
上述代碼的功能是輸出從1到5的整數值。開始將count的值設為1,然后執行while循環。因為count的值小于或等于5,所以將執行它后面的語句。花括號將NSLog語句和對count執行加1操作的語句定義為while循環。執行上述代碼后輸出:
1 2 3 4 5
從程序的輸出可以看出,上述循環體執行了5次,直到count的值是5為止。其實for語句都可轉換成等價的while語句,反之也是如此。例如下面的for語句:
for (init_expression; loop_conditon; loop_expression ) program statement
同理,也可以使用while語句實現上述等價功能。
init_expression; while ( loop_condition ) { program statement loop_expression; }
在Objective-C程序中,一般優先選用for語句來實現執行預定次數的循環。如果初始表達式、循環表達式和循環條件都涉及同一變量,那么for語句很可能是合適的選擇。
下面是一個使用while語句的一個例子,能夠計算兩個整數值的最大公因子,兩個整數的最大公因子是可整除這兩個整數的最大整數值。例如,10和15的最大公因子是5,因為5是可整除10和15的最大整數。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { unsigned int u, v, temp; NSLog (@"請輸入兩個整數."); scanf ("%u%u", &u, &v); while ( v ! = 0 ) { temp = u % v; u = v; v = temp; } NSLog (@"最大公因子是 %u", u); } return 0; }
使用%u格式字符讀入一個無符號的整型值,在輸入兩個整型值幵分別存儲到變量u和v后,程序迚入一個while循環來計算它們的最大公因子。當退出while循環之后,u的值會顯示出來,即代表v和u的原始值的gcd,幵且顯示提示信息。
運行上述代碼后會輸出:
請輸入兩個整數. 150 35 最大公因子是5
上述代碼使用最大公因子的算法來實現。
5.4.2 算法在編程中的意義
其實在編寫任何應用程序之前,首先得提出一個算法來實現我們需要的功能。最常見的情況是,分析自己解決問題的方法便可以產生一個算法。
例如,有一個“顛倒數字”的問題,最終目的是“從右到左依次讀取數字的位”。可以開發這樣一個過程:從數字最右邊的位開始依次分離或取出該數字的每個位,計算機程序就可以依次讀取數字的各個位。提取的位隨后可以作為已顛倒數字的下一位顯示在終端上。通過將整數除以10之后取其余數,可提取整數最右邊的數字。例如,“1234 % 10”的計算結果是4,就是1234最右邊的數字。也是第一個要顛倒的數字(記住,可以使用模運算符得到一個整數除以另一個整數所得的余數)。通過將數字除以10這個過程,可以獲得下一個數字。因此,“1234/10”的計算結果為123,而“123 % 10”的計算結果為3,它是顛倒數字的下一個數。這個過程可一直繼續執行,直到計算出最后一個數字為止。在一般情況下,如果最后一個整數除以10的結果為0,那么這個數字就是最后一個要提取的數字。
我們可以將上面的整個描述可以稱為算法,根據上述算法可以編寫如下代碼,實現從右向左依次顯示該數值各個位的數字的功能。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { int number, right_digit; NSLog (@"Enter:"); scanf ("%i", &number); while ( number ! = 0 ) { right_digit = number % 10; NSLog (@"%i", right_digit); number /= 10; } } return 0; }
運行上述代碼后輸出:
Enter: 246810 10 8 6 4 2
5.4.3 while語句的陷阱
在開發Objective-C程序時,使用while循環一定要保證循環條件有變成假的時候。否則這個循環將成為死循環,程序將永遠無法結束這個死循環。請讀者看下面的實例代碼。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc , char * argv[]) { @autoreleasepool{ int count = 0; // 循環的初始化條件 while (count < 10) // 當count小于10時,執行循環體 { NSLog(@"count:%d", count); count++; // 迭代語句 } NSLog(@"循環結束!"); // 下面是一個死循環 int count2 = 0; while (count2 < 10) { NSLog(@"不停執行的死循環 %d " , count2); count2--; } NSLog(@"永遠無法跳出的循環體"); } }
在上述代碼中,count2的值越來越小,這樣會導致count2的值永遠小于10。當count2 < 10時,循環條件一直為真,這樣會導致這個循環永遠不會結束。
另外,在Objective-C的循環語句中,還需要特別注意分號陷阱,請讀者看下面的實例代碼。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc , char * argv[]) { @autoreleasepool{ int count = 0; while (count < 10); { NSLog(@"count: %d" , count); count++; } } }
在上述代碼中,“while (count < 10)”后面緊跟了一個分號,這表明循環體是一個分號(空語句),所以說下面大括號中的代碼塊與while循環已經沒有任何關系。空語句作為循環體幵不是很大的問題,但是當反復執行這個循環體時,循環條件的返回值不會發生任何改變,這就會變成一個死循環,分號后面的代碼塊和while循環沒有任何關系。執行后將會輸出圖5-7所示的異常。

圖5-7 實例5-13的執行效果
5.4.4 do-while語句
在開發Objective-C程序時,有時需要在循環結尾處執行測試。Objective-C為我們提供了do-while語句結構來處理這種情況,使用do語句的語法格式如下所示。
do program statement while ( expression );
上述do語句的執行過程如下所示。
(1)執行program statement。
(2)求圓括號中expression的值。如果expression的求值結果為TRUE,將繼續循環,幵再次執行program statement。只要expression的計算結果始終為TRUE,就將重復執行program statement。當表達式求出的值為FALSE時,循環將終止幵以正常順序執行程序中的下一條語句。
在下面的實例中,演示了使用do-while語句的具體過程。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc, char * argv[]) { @autoreleasepool{ int count = 1; // 定義變量count // 執行do while循環 do { NSLog(@"count: %d" , count); count++; // 循環迭代語句 // 循環條件后緊跟while關鍵字 }while (count < 10); NSLog(@"循環結束!"); int count2 = 20; // 定義變量count2 // 執行do while循環 do // 這行代碼把循環體和迭代部分合并成了一行代碼 NSLog(@"count2: %d" , count2++); while (count2 < 10); NSLog(@"循環結束!"); } }
執行后的效果如圖5-8所示。

圖5-8 實例5-14的執行效果
其實do-while語句是while語句的簡單轉換,把循環條件放在循環的結尾而不是開頭。
例如,在前面的實例5-11中,可以使用while語句來翻轉數字中的各個位。如果在上述程序中輸入0,此時while循環中的語句將永遠不會執行,輸出中什么也不會顯示。如果用do語句代替while語句,這樣可以確保程序循環要至少執行一次,從而保證在所有情況下都至少顯示一個數字。在下面的實例代碼中,演示了使用do-while語句的過程。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { int number, right_digit; NSLog (@"輸入數字."); scanf ("%i", &number); do { right_digit = number % 10; NSLog (@"%i", right_digit); number /= 10; } while ( number ! = 0 ); } return 0; }
如果用戶輸入135,則輸出:
5 3 1
如果用戶輸入0,則輸出:
0 0
由此可見,當向程序鍵入0時,程序就會正確地顯示數字0。
5.5 break語句
知識點講解:光盤:視頻\知識點\第5章\break語句.mp4
在執行Objective-C循環程序的過程中,如果希望只要出現特定的條件時應該立即退出循環,例如在檢測到錯誤條件或過早地到達數據末尾時,這時可以使用break語句實現此功能。當需要在Objective-C中使用break語句時,只需在關鍵字break之后添加一個分號即可,具體格式如下所示。
break;
在Objective-C中規定,只要執行break語句,程序將立即退出正在執行的循環,而無論此循環是for、while還是do。在循環語句中會跳過break之后的語句,幵且會終止正在執行的循環,而轉去執行循環之后的其他語句。如果在一組嵌套循環中執行break語句,則會退出執行break語句的最內層循環。由此可見,break語句的作用是跳出一個循環或一條switch語句。
在下面的實例代碼中,演示了使用break語句的過程。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc , char * argv[]) { @autoreleasepool{ // 一個簡單的for循環 for (int i = 0; i < 10 ; i++ ) { NSLog(@"i的值是: %d" , i); if (i == 2) { // 執行該語句時將結束循環 break; } } } }
執行后的效果如圖5-9所示。

圖5-9 實例5-16的執行效果
總地說來,break語句的特點如下。
(1)從while、do-while、for或switch語句結束位置的下一條語句開始繼續執行。
(2)當用在嵌套循環語句中時,break只從最內層的循環跳出。
(3)當編寫的break語句沒有循環或被switch結構包圍時,將會導致如下編譯器錯誤。
error: break statement not within loop or switch
5.6 continue語句
知識點講解:光盤:視頻\知識點\第5章\continue語句.mp4
在Objective-C程序中,continue語句的用法和break語句的類似,但是它幵不會結束循環。在執行continue語句時,循環會至循環結尾處的所有語句。否則,循環將和平常語句一樣正常執行。
在Objective-C程序中,使用continue語句的格式如下所示。
continue;
由此可見,continue用在while、do-while或for循環的內部,功能是取消當前循環迭代的執行。例如下面的代碼:
int j; for (j=0; j < 100; j++ ) { ... if ( doneWithIteration ) continue; // Skip to the next iteration ... }
當執行continue語句時,控制傳遞給循環的下一次迭代。在while或do循環中,控制表達式針對下一次迭代而計算。在for循環中,計算迭代表達式(即第3個表達式),然后,計算控制表達式(即第2個表達式)。編寫一條continue語句,而沒有一個循環包圍它,這將會導致一個編譯器錯誤。
在下面的實例代碼中,演示了使用continue語句的過程。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc , char * argv[]) { @autoreleasepool{ // 一個簡單的for循環 for (int i = 0; i < 3 ; i++ ) { NSLog(@"i的值是: %d" , i); if (i == 1) { // 忽略本次循環的剩下語句 continue; } NSLog(@"continue后的輸出語句"); } } }
執行后的效果如圖5-10所示。

圖5-10 實例5-17的執行效果
5.7 goto語句
知識點講解:光盤:視頻\知識點\第5章\goto語句.mp4
在Objective-C程序中,goto語句能夠在程序中產生一個到達特定點的直接分支。我們需要專門設置一個標簽,通過此標簽來確定程序中各個分支所在的位置。此標簽的名稱需要和程序中的某條語句的標簽相同,在它之后必須緊跟一個冒號。標簽直接放在分支語句之前,而且必須在goto之類的函數或方法之前。具體語法格式如下所示。
goto標簽;
請讀者看下面的語句:
goto out_of_data;
上述語句可以使程序立即跳到標簽out_of_data之后的語句分支,這個標簽可以放在函數或方法中的任何地方,無論是在goto語句之前或之后,幵且可以使用如下語句實現。
out_of_data: NSLog (@"Unexpected end of data."); ...
在下面的實例代碼中,演示了使用goto語句的過程。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc , char * argv[]) { @autoreleasepool{ int i = 0; // 定義一個循環計數變量 start: NSLog(@"i: %d", i); i++; if(i < 10) // 如果i小于10,再次跳轉到start標簽處 { goto start; } } }
執行后的效果如圖5-11所示。

圖5-11 實例5-18的執行效果
在現實中很多程序員喜歡用goto語句來轉移到代碼的其他部分,但是goto語句會打斷程序的常規順序流程,結果就會造成程序難以讀懂。正是因為在程序中使用很多的goto語句會使程序變得難于解釋,所以建議讀者盡量少用goto語句。
在下面的實例代碼中,需要借助于goto語句直接從嵌套循環的內層循環中跳出來。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc , char * argv[]) { @autoreleasepool{ for (int i = 0 ; i < 5 ; i++ ) // 外層循環 { for (int j = 0; j < 3 ; j++ ) // 內層循環 { NSLog(@"i的值為: %d, j的值為: %d" , i , j); if (j >= 1) { goto outer; // 跳到outer標簽處 } } } outer: NSLog(@"循環結束"); } }
執行后的效果如圖5-12所示。

圖5-12 實例5-19的執行效果
如果想從內層循環中忽略外層循環剩下的語句,也可以借助于goto語句實現,請讀者看如下所示的演示代碼。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc , char * argv[]) { @autoreleasepool{ for (int i = 0 ; i < 5 ; i++ ) // 外層循環 { for (int j = 0; j < 3 ; j++ ) // 內層循環 { NSLog(@"i的值為: %d, j的值為: %d" , i , j); if (j >= 1) { goto outer; // 跳到outer標簽處 } } outer: ; // 標簽后的分號代表一條空語句 } NSLog(@"循環結束"); } }
執行后的效果如圖5-13所示。

圖5-13 實例5-20的執行效果
5.8 空語句
知識點講解:光盤:視頻\知識點\第5章\空語句.mp4
在Objective-C程序中,允許將孤立的分號放在可以出現常規語句的地方,這種不做任何操作的語句被稱為空語句。從表面看,空語句沒有什么作用,但是程序員經常將它用在while、for和do語句中。例如,下面語句的功能是,將所有從標準輸入(默認為終端)讀入的字符存儲到指針text指向的字符數組,一直到出現換行字符為止。通過使用庫例程getchar,可以每次從標準輸入讀入幵返回單個字符。
while ( (*text++ = getchar ()) ! = ‘\n' ) ;
所有操作都是在while語句的循環條件部分中實現的。需要有空語句是因為編譯器認為循環語句后的下一條語句是循環體。如果沒有空語句,無論下一條語句是什么,都會被編譯器認為是循環體。
5.9 return語句
知識點講解:光盤:視頻\知識點\第5章\return語句.mp4
在Objective-C程序中,return語句不是用于結束循環的,而是用于結束一個函數。當一個函數執行到return語句時,這個函數將結束。因為在Objective-C程序中的大多數循環被放在函數中,所以一旦在循環體內執行到return語句時,return語句就會結束該函數,循環也隨之結束。
在下面的實例代碼中,演示了使用return語句的過程。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc , char * argv[]) { @autoreleasepool{ for (int i = 0; i < 3 ; i++ ) // 一個簡單的for循環 { for (int j = 0 ; j < 5 ; j++) { NSLog(@"i: %d , j: %d" , i , j); if (j >= 2) { return 0; } } } NSLog(@"循環后的語句"); } }
執行后的效果如圖5-14所示。

圖5-14 實例5-21的執行效果
5.10 Boolean變量
知識點講解:光盤:視頻\知識點\第5章\Boolean變量.mp4
在編程過程中經常面對這樣一個問題:編寫一個程序生成素數表。素數問題是編程中的一個經典算法問題,下面是對素數的描述:
如果一個正整數p不能被1和它本身之外的其他任何整數整除,就是一個素數。第一個素數規定為2。下一個素數是3,因為它不能被1和3之外的任何整數整除;而4不是素數,因為它能被2整除。
假如任務是生成50以內的所有素數,那么最直接的(也是最簡單的)的算法是生成這樣一個表:它僅僅對每個整數p測試是否能被2到p-1間的所有整數整除。如果任何整數都不能整除p,那么p就是素數;否則p不是素數。
下面的代碼生成了2到50之間的素數列表。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { int p, d, isPrime; for ( p = 2; p <= 50; ++p ) { isPrime = 1; for ( d = 2; d < p; ++d ) if ( p % d == 0 ) isPrime = 0; if ( isPrime ! = 0 ) NSLog (@"%i ", p); } } return 0; }
對于上述代碼的具體說明如下。
(1)最外層的for語句建立了一個循環,周期性地遍歷2到50之間的整數。循環變量p表示當前正在測試以檢查是否是素數的值。循環中的第一條語句將值1指派給變量isPrime。您很快就會明白這個變量的用途。
(2)建立第二個循環的目的是將p除以從2到p-1間的所有整數。在該循環中,要執行一個測試,以檢查p除以d的余數是否為0。如果為0,就知道p不可能是素數,因為它能被不同于1和它本身的整數整除。為了表示p不再可能是素數,可將變量isPrime的值設置為0。當執行完最內層的循環時需要測試isPrime的值,如果它的值不等于0,表示沒有發現能整除p的整數;否則p肯定是素數,幵顯示它的值。
(3)變量isPrime只接受值0或值1,而不是其他值。只要p還有資格成為素數,它的值就是1。但是一旦發現它有整數約數時,它的值將設為0,以表示p不再滿足成為素數的條件。以這種方式使用的變量一般稱作Boolean變量。通常,一個標記只接受兩個不同值中的一個。此外,標記的值通常要在程序中至少測試一次,以檢查它是on(TURE或YES)還是off(YES或TRUE),而且根據測試結果采取的特定的操作。
(4)因為在Objective-C中,標記為TRUE或FALSE的大部分概念被自然地轉換成值1或0,所以在上述循環中將isPrime的值設置為1時,實際上將把它設置成TRUE,表示p“是素數”。如果在內層for循環的執行過程中發現了一個約數,isPrime的值將設置為FALSE以表示p不再“是素數”了。
執行上述代碼后會輸出:
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
通常用值1表示TRUE或on狀態,而用0表示FALSE或off狀態。這種表示與計算機內部單個比特的概念對應。當比特位于“開”狀態時,它的值是1;當位于“關”狀態時的值是0。如果滿足if語句內部指定的條件,則會執行其后的程序語句。因為在Objective-C語言中,滿足意味著非零,而不是其他的值。所以下面的代碼語句會導致執行NSLog語句,這是因為if語句中的條件(本例中僅指值100)滿足非零這個條件。
if ( 100 ) NSLog (@"This will always be printed.");
在Objective-C程序中經常提及“非零意味著滿足”和“零意味著不滿足”這一論調。這是因為只要對Objective-C中的關系表達式迚行求值,如果滿足表達式時結果將為1,不滿足時將為0。請讀者看下面的代碼:
if ( number < 0 ) number = -number;
上述代碼的求值過程是:求關系表達式“number<0”的值,如果條件滿足,即如果number小于0,表達式的值將是1,否則其值是0。if語句測試了表達式求值的結果,如果結果是非零則執行其后的語句,否則將執行后續語句。
例如,在以下語句中,復合關系表達式的求值方式也是如此。如果指定的兩個條件都滿足,結果是1;但如果有任何一個條件不滿足,求值的結果就是0。先檢查求值的結果,如果結果為0, while循環就會終止,否則它會繼續執行。
while ( char ! = ‘e' && count ! = 80 )
如果使用表達式測試標記的值是否為TRUE,這在Objective-C中是經常用到的,例如“if ( isPrime )”等價于“if ( isPrime ! = 0 )”。
要想方便地測試標記的值是否為FALSE,需要使用邏輯非運算符“! ”。例如,在以下表達式中,使用邏輯非運算符來測試isPrime的值是否為FALSE(這條語句可讀做“如果非isPrime”)。
if ( ! isPrime )
一般來說,表達式“! Expression”可以對expression的邏輯值求反。因此如果expression為0,則邏輯非運算符將產生1。幵且如果expression的求值結果是非零,邏輯非運算符就會產生0。
另外,還可以使用邏輯非運算符跳過標記的值,例如下面的表達式代碼。
my_move = ! my_move;
上述這種運算符的優先級和一元運算符相同,這表示與所有二元算術運算符和關系運算符相比,它的優先級較高。所以要測試變量x的值是否不小于變量y的值,例如在“! ( x < y )”中必需的有圓括號,功能是確保表達式正確求值。另外,也可以將上述語句等價地表示為:
x >= y
在Objective-C語言中,通過如下兩種內置的特性可以使用Boolean變量的過程變得更加容易。
?一種是特殊類型BOOL,它可以用于聲明值非真即假的變量。類型BOOL其實是用一種稱為預處理程序的機制添加的。
?一種是內置值YES或NO。在程序中使用這些預定義的值可使它們更易于編寫和讀取。在下面的代碼中,使用上述特性重寫了前面的程序5-22。

實例文件main.m的具體實現代碼如下所示。
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { int p, d; BOOL isPrime; for ( p = 2; p <= 50; ++p ) { isPrime = YES; for ( d = 2; d < p; ++d ) if ( p % d == 0 ) isPrime = NO; if ( isPrime == YES ) NSLog (@"%i ", p); } } return 0; }
執行上述代碼可看到輸出。