Swift總結3:控制流

控制流

for循環(huán)

for-in

for

while循環(huán)

while

repeat-while

條件語句

if

switch

不存在隱式的貫穿

區(qū)間匹配

元組

值綁定

where

控制轉移語句

continue

break

循環(huán)語句中的break

switch語句中的break

fallthrough

帶標簽的語句

提前退出

檢測API可用性

控制流

Swift提供了類似C語言的流程控制結構,包括可以多次執(zhí)行任務的for和while循環(huán),基于特定條件選擇執(zhí)行不同代碼分支的if、guard和switch語句,還有控制流程跳轉到其他代碼的break和continue語句。

除了C語言里面?zhèn)鹘y(tǒng)的for循環(huán),Swift還增加了for-in循環(huán),用來更簡單地遍歷數(shù)組(array),字典(dictionary),區(qū)間(range),字符串(string)和其他序列類型。

Swift的switch語句比C語言中更加強大。在C語言中,如果某個case不小心漏寫了break,這個case就會貫穿至下一個case,Swift無需寫break,所以不會發(fā)生這種貫穿的情況。case還可以匹配更多的類型模式,包括區(qū)間匹配(range matching),元組(tuple)和特定類型的描述。switch的case語句中匹配的值可以是由case體內(nèi)部臨時的常量或者變量決定,也可以由where分句描述更復雜的匹配條件。

for循環(huán)

Swift提供了兩種for循環(huán)形式

for-in循環(huán)對一個集合里面的每個元素執(zhí)行一系列語句。

for循環(huán),用來重復執(zhí)行一系列語句直到達成特定條件達成,一般通過在每次循環(huán)完成后增加計數(shù)器的值來實現(xiàn)。

for-in

使用for-in循環(huán)來遍歷一個集合里面的所有元素,例如由數(shù)字表示的區(qū)間、數(shù)組中的元素、字符串中的字符。

foriin1...5{print("/(i)", terminator:"-")}

上面的i是一個每次循環(huán)遍歷開始時被自動賦值的常量。這種情況下,i在使用前不需要聲明,只需要將它包含在循環(huán)的聲明中,就可以對其進行隱式聲明,而無需使用let關鍵字聲明

如果你不需要知道區(qū)間序列內(nèi)每一項的值,你可以使用下劃線(_)替代變量名來忽略對值的訪問。

for_in0..<5{print("Hello")}

使用for-in遍歷數(shù)組和字典。遍歷字典時,字典的每項元素會以(key, value)元組的形式返回,你可以在for-in循環(huán)中使用顯式的常量名稱來解讀(key, value)元組。

leta = [1,2,3]foriina {//注意:文檔上說i是常量,但是貌似只能寫var不能寫letprint(i)}letb = ["A":1,"B":2,"C":3]forvar(k, v)inb {//注意:文檔上說k和v是常量,但是貌似只能寫var不能寫letprint("/(k) => /(v)")}

for

除了for-in循環(huán),Swift提供使用條件判斷和遞增方法的標準C樣式for循環(huán)。

forvari =0; i <3; ++i {//注意:此時的var必須寫,而且不能寫成letprint(i)}

在初始化表達式中聲明的常量和變量(比如var i = 0)只在for循環(huán)的生命周期里有效。如果想在循環(huán)結束后訪問i的值,你必須要在循環(huán)生命周期開始前聲明i。

while循環(huán)

這類循環(huán)適合使用在第一次迭代前迭代次數(shù)未知的情況下。Swift提供兩種while循環(huán)形式:

while循環(huán),每次在循環(huán)開始時計算條件是否符合

repeat-while循環(huán),每次在循環(huán)結束時計算條件是否符合

while

while循環(huán)從計算單一條件開始。如果條件為true,會重復運行一系列語句,直到條件變?yōu)閒alse。

/** * 文檔上的蛇和梯子的小游戲,也叫做滑道和梯子 */let finalSquare =25varboard = [Int](count: finalSquare +1, repeatedValue:0)board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08varsquare =0vardiceRoll =0whilesquare < finalSquare {// 擲骰子if++diceRoll ==7{ diceRoll =1}// 根據(jù)點數(shù)移動square += diceRollifsquare < board.count {// 如果玩家還在棋盤上,順著梯子爬上去或者順著蛇滑下去square += board[square]? ? }}print("Game over!")print("最終位置是/(square)。")// 最終位置是27。

repeat-while

它和while的區(qū)別是在判斷循環(huán)條件之前,先執(zhí)行一次循環(huán)的代碼塊,然后重復循環(huán)直到條件為false。Swift語言的repeat-while循環(huán)和其他語言中的do-while循環(huán)是類似的。

/*** 文檔上的蛇和梯子的小游戲,也叫做滑道和梯子*/let finalSquare =25varboard = [Int](count: finalSquare +1, repeatedValue:0)board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08varsquare =0vardiceRoll =0repeat {// 順著梯子爬上去或者順著蛇滑下去square += board[square]// 擲骰子if++diceRoll ==7{ diceRoll =1}// 根據(jù)點數(shù)移動square += diceRoll}whilesquare < finalSquareprint("Game over!")print("最終位置是/(square)。")// 最終位置是27。

可以看出,此時repeat-while表現(xiàn)得比while循環(huán)更好。repeat-while方式會在條件判斷square沒有超出后直接運行square += board[square],這種方式可以去掉while版本中的數(shù)組越界判斷

條件語句

Swift提供兩種類型的條件語句:if語句和switch語句。通常,當條件較為簡單且可能的情況很少時,使用if語句。而switch語句更適用于條件較復雜、可能情況較多且需要用到模式匹配(pattern-matching)的情境。

if

if語句最簡單的形式就是只包含一個條件,當且僅當該條件為true時,才執(zhí)行相關代碼。

if語句允許二選一,也就是當條件為false時,執(zhí)行else語句。

還可以把多個if語句鏈接在一起,此時,最后的else語句是可選的

leta =10ifa <10{print("a < 10")}elseifa >10{print("a > 10")}else{print("a == 10")}

switch

switch語句會嘗試把某個值與若干個模式(pattern)進行匹配。根據(jù)第一個匹配成功的模式,switch語句會執(zhí)行對應的代碼。當有可能的情況較多時,通常用switch語句替換if語句。

switch語句必須是完備的。這就是說,每一個可能的值都必須至少有一個case分支與之對應。在某些不可能涵蓋所有值的情況下,你可以使用默認(default)分支滿足該要求,這個默認分支必須在switch語句的最后面。當switch語句不完備時,默認(default)分支必須有

不存在隱式的貫穿

與C語言和Objective-C中的switch語句不同,在Swift中,當匹配的case分支中的代碼執(zhí)行完畢后,程序會終止switch語句,而不會繼續(xù)執(zhí)行下一個case分支。這也就是說,不需要在case分支中顯式地使用break語句。這使得switch語句更安全、更易用,也避免了因忘記寫break語句而產(chǎn)生的錯誤。

雖然在Swift中break不是必須的,但你依然可以在case分支中的代碼執(zhí)行完畢前使用break跳出

每一個 case 分支都必須包含至少一條語句,避免了意外地從一個case分支貫穿到另外一個,使得代碼更安全、也更直觀。

一個case也可以包含多個模式,用逗號把它們分開(如果太長了也可以分行寫)。

leta =1switcha {//case0://每個case分支不能有空語句case1:print("1")case2,3:print("2 or 3")default://此時switch還不完備,因此必須有default分支print("Error")}

區(qū)間匹配

case 分支的模式也可以是一個值的區(qū)間。

閉區(qū)間操作符(...)以及半開區(qū)間操作符(..<)功能被重載去返回IntervalType或Range。一個區(qū)間可以決定他是否包含特定的元素,就像當匹配一個switch聲明的case一樣。區(qū)間是一個連續(xù)值的集合,可以用for-in語句遍歷它。

let a =1switcha {case0...1:print("0 ~ 1")case2..<10:print("2 ~ 9")default:// 必須有default分支print("other")}

元組

可以使用元組在同一個switch語句中測試多個值。元組中的元素可以是值,也可以是區(qū)間。另外,使用下劃線(_)來匹配所有可能的值。

let a = (1,1)switcha {case(0,0):print("在原點")case(_,0):print("在x軸")case(0, _):print("在y軸")case(-1...1, -1...1):print("在格子里")// 在格子里default:print("在格子外")}let b = (0,0)switchb {case(0,0):print("在原點")// 在原點,優(yōu)先匹配上!case(_,0):print("在x軸")case(0, _):print("在y軸")case(-1...1, -1...1):print("在格子里")default:print("在格子外")}

不像C語言,Swift允許多個case匹配同一個值。實際上,在這個例子中,點(0, 0)可以匹配所有四個case。但是,如果存在多個匹配,那么只會執(zhí)行第一個被匹配到的case分支。考慮點(0, 0)會首先匹配case(0, 0),因此剩下的能夠匹配(0, 0)的case分支都會被忽視掉。

值綁定

case分支的模式允許將匹配的值綁定到一個臨時的常量或變量,這些常量或變量在該case分支里就可以被引用了——這種行為被稱為值綁定(value binding)。

leta = (0,1)switcha {case(letx,0):print("1、/(x)")case(0,letx):print("2、/(x)")//2、1caselet(x, y):print("3、/(x),/(y)")//不需要default,因為已經(jīng)完備}letb = (3,0)switchb {case(letx,0)://1、3print("1、/(x)")case(0,letx):print("2、/(x)")caselet(x, y):print("3、/(x),/(y)")}letc = (23, -12)switchc {case(letx,0):print("1、/(x)")case(0,letx):print("2、/(x)")caselet(x, y):print("3、/(x),/(y)")//3、23,-12}letd = (0,0)switchd {case(letx,0):print("1、/(x)")//1、0case(0,letx):print("2、/(x)")caselet(x, y):print("3、/(x),/(y)")}

這三個case都聲明了常量x和y的占位符,用于臨時獲取元組中的一個或兩個值。一旦聲明了這些臨時的常量,它們就可以在其對應的case分支里引用。

第一個case將匹配一個縱坐標為0的點,并把這個點的橫坐標賦給臨時的常量x。類似的,第二個case將匹配一個橫坐標為0的點,并把這個點的縱坐標賦給臨時的常量y。

這個switch語句不包含默認分支。這是因為最后一個case聲明了一個可以匹配余下所有值的元組。這使得switch語句已經(jīng)完備了!

這里x和y是常量,這是因為沒有必要在其對應的case分支中修改它們的值。然而,它們也可以是變量,那么程序將會創(chuàng)建臨時變量,并用相應的值初始化它。修改這些變量只會影響其對應的case分支。

where

case分支的模式可以使用where語句來判斷額外的條件。

leta = (1, -1)switcha {caselet(x, y)wherex == y:? ? print("x == y")caselet(x, y)wherex == -y:? ? print("x == -y")// x == -ycaselet(x, y):// 已經(jīng)完備,無需defaultprint(a)}

上面聲明的常量x和y被用作where語句的一部分,從而創(chuàng)建一個動態(tài)的過濾器(filter)。當且僅當where語句的條件為true時,匹配到的case分支才會被執(zhí)行。

控制轉移語句

控制轉移語句改變你代碼的執(zhí)行順序,通過它你可以實現(xiàn)代碼的跳轉。Swift有五種控制轉移語句

continue

break

fallthrough

return(“函數(shù)”一節(jié)中介紹)

throw(“錯誤拋出”一節(jié)中介紹)

continue

continue語句告訴一個循環(huán)體立刻停止本次循環(huán)迭代,重新開始下次循環(huán)迭代。

在一個帶有條件和遞增的for循環(huán)體中,調用continue語句后,迭代增量仍然會被計算求值。循環(huán)體繼續(xù)像往常一樣工作,僅僅只是循環(huán)體中的執(zhí)行代碼會被跳過。

break

break語句會立刻結束整個控制流的執(zhí)行。當你想要更早的結束一個switch代碼塊或者一個循環(huán)體時,你都可以使用break語句。

循環(huán)語句中的break

當在一個循環(huán)體中使用break時,會立刻中斷該循環(huán)體的執(zhí)行,然后跳轉到表示循環(huán)體結束的大括號(})后的第一行代碼。不會再有本次循環(huán)迭代的代碼被執(zhí)行,也不會再有下次的循環(huán)迭代產(chǎn)生。

switch語句中的break

當在一個switch代碼塊中使用break時,會立即中斷該switch代碼塊的執(zhí)行,并且跳轉到表示switch代碼塊結束的大括號(})后的第一行代碼。

這種特性可以被用來匹配或者忽略一個或多個分支。因為Swift的switch需要包含所有的分支而且不允許有為空的分支,有時為了使你的意圖更明顯,需要特意匹配或者忽略某個分支。那么當你想忽略某個分支時,可以在該分支內(nèi)寫上break語句。當那個分支被匹配到時,分支內(nèi)的break語句立即結束switch代碼塊。

注意:當一個switch分支僅僅包含注釋時,會被報編譯時錯誤。注釋不是代碼語句而且也不能讓switch分支達到被忽略的效果。你總是可以使用break來忽略某個分支。

fallthrough

如果你確實需要C風格的貫穿的特性,你可以在每個需要該特性的case分支中使用fallthrough`關鍵字。

fallthrough關鍵字不會檢查它下一個將會落入執(zhí)行的case中的匹配條件。fallthrough簡單地使代碼執(zhí)行繼續(xù)連接到下一個case中的執(zhí)行代碼,這和C語言標準中的switch語句特性是一樣的。

let a =3switcha {case0,1,2:print("0 ~ 2")case3..<100:print("3 ~ 100")// 3 ~ 100fallthroughdefault:print("Over")// Over}

帶標簽的語句

在Swift中,你可以在循環(huán)體和switch代碼塊中嵌套循環(huán)體和switch代碼塊來創(chuàng)造復雜的控制流結構。然而,循環(huán)體和switch代碼塊兩者都可以使用break語句來提前結束整個方法體。因此,顯式地指明break語句想要終止的是哪個循環(huán)體或者switch代碼塊,會很有用。類似地,如果你有許多嵌套的循環(huán)體,顯式指明continue語句想要影響哪一個循環(huán)體也會非常有用。

可以使用標簽來標記一個循環(huán)體或者switch代碼塊,當使用break或者continue時,帶上這個標簽,可以控制該標簽代表對象的中斷或者執(zhí)行。產(chǎn)生一個帶標簽的語句是通過在該語句的關鍵詞的同一行前面放置一個標簽,并且該標簽后面還需帶著一個冒號。

/** * 文檔上的蛇和梯子的小游戲,也叫做滑道和梯子。 * 現(xiàn)在增加一個條件:為了獲勝,你必須剛好落在第 25 個方塊中。即如果某次擲骰子使你的移動超出第 25 個方塊,你必須重新擲骰子,直到你擲出的骰子數(shù)剛好使你能落在第 25 個方塊中。 */let finalSquare =25varboard = [Int](count: finalSquare +1, repeatedValue:0)board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08varsquare =0vardiceRoll =0gameLoop:whilesquare != finalSquare {if++diceRoll ==7{ diceRoll =1}? ? switch square + diceRoll {casefinalSquare:// 到達最后一個方塊,游戲結束print(square + diceRoll)breakgameLoopcaselet newSquare where newSquare > finalSquare:// 超出最后一個方塊,再擲一次骰子print("square = /(square), diceRoll = /(diceRoll)")continuegameLoopdefault:// 本次移動有效square += diceRoll? ? ? ? square += board[square]? ? }}print("Game over!")/*過程是:23+1 => 24-8 => 16+2 => 18+3 => 21+4 => 25square = 23, diceRoll = 4square = 23, diceRoll = 5square = 23, diceRoll = 625Game over!*/

提前退出

像if語句一樣,guard的執(zhí)行取決于一個表達式的布爾值。我們可以使用guard語句來要求條件必須為真時,以執(zhí)行guard語句后的代碼。不同于if語句,一個guard語句總是有一個else分句,如果條件不為真則執(zhí)行else分句中的代碼。

如果guard語句的條件被滿足,則在保護語句的封閉大括號結束后繼續(xù)執(zhí)行代碼。任何使用了可選綁定作為條件的一部分并被分配了值的變量或常量對于剩下的保護語句出現(xiàn)的代碼段是可用的。

如果條件不被滿足,在else分支上的代碼就會被執(zhí)行。這個分支必須轉移控制以退出guard語句出現(xiàn)的代碼段。它可以用控制轉移語句如return,break,continue或者throw做這件事,或者調用一個不返回的方法或函數(shù),例如fatalError()。

相比于可以實現(xiàn)同樣功能的if語句,按需使用guard語句會提升我們代碼的可靠性。 它可以使你的代碼連貫的被執(zhí)行而不需要將它包在else塊中,它可以使你處理違反要求的代碼使其接近要求。

let a =10/*guard a <5else{? ? ? //error:'guard'body maynotfall through, consider using'return'or'break'to exit the scopeprint("a >= 5")}*/whiletrue{? ? guard a <5else{print("a >= 5")? ? // a >=5break// 必須寫break,否則報上面的錯誤? ? }}print("...")

檢測API可用性

Swift有檢查API可用性的內(nèi)置支持,這可以確保我們不會不小心地使用對于當前部署目標不可用的API。

編譯器使用SDK中的可用信息來驗證我們的代碼中使用的所有API在項目指定的部署目標上是否可用。如果我們嘗試使用一個不可用的API,Swift會在編譯期報錯。

我們使用一個可用性條件在一個if或guard語句中去有條件地執(zhí)行一段代碼,這取決于我們想要使用的API是否在運行時是可用的。編譯器使用從可用性條件語句中獲取的信息去驗證在代碼塊中調用的API是否都可用。

if#available(iOS 9, OSX 10.10, *) {print("iOS 9+ or OS X 10.10+")// 條件是或的關系,有一個平臺滿足就可以,表示指定了在iOS系統(tǒng)上,if段的代碼僅會在iOS 9及更高版本的系統(tǒng)上執(zhí)行;在OS X,僅會在OS X v10.10及更高版本的系統(tǒng)上執(zhí)行。}else{print("old version")}

可用性條件指定了在iOS系統(tǒng)上,if段的代碼僅會在iOS 9及更高版本的系統(tǒng)上執(zhí)行;在OS X,僅會在OS X v10.10及更高版本的系統(tǒng)上執(zhí)行。上面的最后一個參數(shù)*是必須寫的,用于處理未來潛在的平臺??捎眯詶l件獲取了一系列平臺名字和版本。平臺名字可以是iOS,OSX或watchOS。除了特定的主板本號像iOS 8,我們可以指定較小的版本號像iOS 8.3以及OS X v10.10.3。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容