控制流
for循環(huán)
for-in
for
while循環(huán)
while
repeat-while
條件語句
if
switch
不存在隱式的貫穿
區(qū)間匹配
元組
值綁定
where
控制轉(zhuǎn)移語句
continue
break
循環(huán)語句中的break
switch語句中的break
fallthrough
帶標(biāo)簽的語句
提前退出
檢測(cè)API可用性
控制流
Swift提供了類似C語言的流程控制結(jié)構(gòu)耙箍,包括可以多次執(zhí)行任務(wù)的for和while循環(huán)撰糠,基于特定條件選擇執(zhí)行不同代碼分支的if、guard和switch語句究西,還有控制流程跳轉(zhuǎn)到其他代碼的break和continue語句窗慎。
除了C語言里面?zhèn)鹘y(tǒng)的for循環(huán),Swift還增加了for-in循環(huán),用來更簡(jiǎn)單地遍歷數(shù)組(array)遮斥,字典(dictionary)峦失,區(qū)間(range),字符串(string)和其他序列類型术吗。
Swift的switch語句比C語言中更加強(qiáng)大尉辑。在C語言中,如果某個(gè)case不小心漏寫了break较屿,這個(gè)case就會(huì)貫穿至下一個(gè)case隧魄,Swift無需寫break,所以不會(huì)發(fā)生這種貫穿的情況隘蝎。case還可以匹配更多的類型模式购啄,包括區(qū)間匹配(range matching),元組(tuple)和特定類型的描述嘱么。switch的case語句中匹配的值可以是由case體內(nèi)部臨時(shí)的常量或者變量決定狮含,也可以由where分句描述更復(fù)雜的匹配條件。
for循環(huán)
Swift提供了兩種for循環(huán)形式
for-in循環(huán)對(duì)一個(gè)集合里面的每個(gè)元素執(zhí)行一系列語句曼振。
for循環(huán)几迄,用來重復(fù)執(zhí)行一系列語句直到達(dá)成特定條件達(dá)成,一般通過在每次循環(huán)完成后增加計(jì)數(shù)器的值來實(shí)現(xiàn)冰评。
for-in
使用for-in循環(huán)來遍歷一個(gè)集合里面的所有元素映胁,例如由數(shù)字表示的區(qū)間、數(shù)組中的元素甲雅、字符串中的字符解孙。
foriin1...5{print("/(i)", terminator:"-")}
上面的i是一個(gè)每次循環(huán)遍歷開始時(shí)被自動(dòng)賦值的常量。這種情況下务荆,i在使用前不需要聲明妆距,只需要將它包含在循環(huán)的聲明中,就可以對(duì)其進(jìn)行隱式聲明函匕,而無需使用let關(guān)鍵字聲明娱据。
如果你不需要知道區(qū)間序列內(nèi)每一項(xiàng)的值,你可以使用下劃線(_)替代變量名來忽略對(duì)值的訪問盅惜。
for_in0..<5{print("Hello")}
使用for-in遍歷數(shù)組和字典中剩。遍歷字典時(shí),字典的每項(xiàng)元素會(huì)以(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提供使用條件判斷和遞增方法的標(biāo)準(zhǔn)C樣式for循環(huán)朴译。
forvari =0; i <3; ++i {//注意:此時(shí)的var必須寫,而且不能寫成letprint(i)}
在初始化表達(dá)式中聲明的常量和變量(比如var i = 0)只在for循環(huán)的生命周期里有效属铁。如果想在循環(huán)結(jié)束后訪問i的值眠寿,你必須要在循環(huán)生命周期開始前聲明i。
while循環(huán)
這類循環(huán)適合使用在第一次迭代前迭代次數(shù)未知的情況下焦蘑。Swift提供兩種while循環(huán)形式:
while循環(huán)盯拱,每次在循環(huán)開始時(shí)計(jì)算條件是否符合
repeat-while循環(huán),每次在循環(huán)結(jié)束時(shí)計(jì)算條件是否符合
while
while循環(huán)從計(jì)算單一條件開始例嘱。如果條件為true狡逢,會(huì)重復(fù)運(yùn)行一系列語句,直到條件變?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ù)點(diǎn)數(shù)移動(dòng)square += diceRollifsquare < board.count {// 如果玩家還在棋盤上,順著梯子爬上去或者順著蛇滑下去square += board[square]? ? }}print("Game over!")print("最終位置是/(square)间学。")// 最終位置是27殷费。
repeat-while
它和while的區(qū)別是在判斷循環(huán)條件之前印荔,先執(zhí)行一次循環(huán)的代碼塊低葫,然后重復(fù)循環(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ù)點(diǎn)數(shù)移動(dòng)square += diceRoll}whilesquare < finalSquareprint("Game over!")print("最終位置是/(square)。")// 最終位置是27水泉。
可以看出善涨,此時(shí)repeat-while表現(xiàn)得比while循環(huán)更好。repeat-while方式會(huì)在條件判斷square沒有超出后直接運(yùn)行square += board[square]草则,這種方式可以去掉while版本中的數(shù)組越界判斷
钢拧。
條件語句
Swift提供兩種類型的條件語句:if語句和switch語句。通常炕横,當(dāng)條件較為簡(jiǎn)單且可能的情況很少時(shí)源内,使用if語句。而switch語句更適用于條件較復(fù)雜份殿、可能情況較多且需要用到模式匹配(pattern-matching)的情境膜钓。
if
if語句最簡(jiǎn)單的形式就是只包含一個(gè)條件,當(dāng)且僅當(dāng)該條件為true時(shí)卿嘲,才執(zhí)行相關(guān)代碼颂斜。
if語句允許二選一,也就是當(dāng)條件為false時(shí)拾枣,執(zhí)行else語句沃疮。
還可以把多個(gè)if語句鏈接在一起盒让,此時(shí),最后的else語句是可選的
leta =10ifa <10{print("a < 10")}elseifa >10{print("a > 10")}else{print("a == 10")}
switch
switch語句會(huì)嘗試把某個(gè)值與若干個(gè)模式(pattern)進(jìn)行匹配司蔬。根據(jù)第一個(gè)匹配成功的模式糯彬,switch語句會(huì)執(zhí)行對(duì)應(yīng)的代碼。當(dāng)有可能的情況較多時(shí)葱她,通常用switch語句替換if語句撩扒。
switch語句必須是完備的。這就是說吨些,每一個(gè)可能的值都必須至少有一個(gè)case分支與之對(duì)應(yīng)搓谆。在某些不可能涵蓋所有值的情況下,你可以使用默認(rèn)(default)分支滿足該要求豪墅,這個(gè)默認(rèn)分支必須在switch語句的最后面泉手。當(dāng)switch語句不完備時(shí),默認(rèn)(default)分支必須有偶器。
不存在隱式的貫穿
與C語言和Objective-C中的switch語句不同斩萌,在Swift中,當(dāng)匹配的case分支中的代碼執(zhí)行完畢后屏轰,程序會(huì)終止switch語句颊郎,而不會(huì)繼續(xù)執(zhí)行下一個(gè)case分支。這也就是說霎苗,不需要在case分支中顯式地使用break語句姆吭。這使得switch語句更安全、更易用唁盏,也避免了因忘記寫break語句而產(chǎn)生的錯(cuò)誤内狸。
雖然在Swift中break不是必須的,但你依然可以在case分支中的代碼執(zhí)行完畢前使用break跳出厘擂。
每一個(gè) case 分支都必須包含至少一條語句昆淡,避免了意外地從一個(gè)case分支貫穿到另外一個(gè),使得代碼更安全刽严、也更直觀昂灵。
一個(gè)case也可以包含多個(gè)模式,用逗號(hào)把它們分開(如果太長(zhǎng)了也可以分行寫)港庄。
leta =1switcha {//case0://每個(gè)case分支不能有空語句case1:print("1")case2,3:print("2 or 3")default://此時(shí)switch還不完備倔既,因此必須有default分支print("Error")}
區(qū)間匹配
case 分支的模式也可以是一個(gè)值的區(qū)間。
閉區(qū)間操作符(...)以及半開區(qū)間操作符(..<)功能被重載去返回IntervalType或Range鹏氧。一個(gè)區(qū)間可以決定他是否包含特定的元素渤涌,就像當(dāng)匹配一個(gè)switch聲明的case一樣。區(qū)間是一個(gè)連續(xù)值的集合把还,可以用for-in語句遍歷它实蓬。
let a =1switcha {case0...1:print("0 ~ 1")case2..<10:print("2 ~ 9")default:// 必須有default分支print("other")}
元組
可以使用元組在同一個(gè)switch語句中測(cè)試多個(gè)值茸俭。元組中的元素可以是值,也可以是區(qū)間安皱。另外调鬓,使用下劃線(_)來匹配所有可能的值。
let a = (1,1)switcha {case(0,0):print("在原點(diǎn)")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("在原點(diǎn)")// 在原點(diǎn)酌伊,優(yōu)先匹配上腾窝!case(_,0):print("在x軸")case(0, _):print("在y軸")case(-1...1, -1...1):print("在格子里")default:print("在格子外")}
不像C語言,Swift允許多個(gè)case匹配同一個(gè)值居砖。實(shí)際上虹脯,在這個(gè)例子中,點(diǎn)(0, 0)可以匹配所有四個(gè)case奏候。但是循集,如果存在多個(gè)匹配,那么只會(huì)執(zhí)行第一個(gè)被匹配到的case分支蔗草≈渫考慮點(diǎn)(0, 0)會(huì)首先匹配case(0, 0),因此剩下的能夠匹配(0, 0)的case分支都會(huì)被忽視掉咒精。
值綁定
case分支的模式允許將匹配的值綁定到一個(gè)臨時(shí)的常量或變量镶柱,這些常量或變量在該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,因?yàn)橐呀?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)")}
這三個(gè)case都聲明了常量x和y的占位符,用于臨時(shí)獲取元組中的一個(gè)或兩個(gè)值甩恼。一旦聲明了這些臨時(shí)的常量,它們就可以在其對(duì)應(yīng)的case分支里引用集畅。
第一個(gè)case將匹配一個(gè)縱坐標(biāo)為0的點(diǎn),并把這個(gè)點(diǎn)的橫坐標(biāo)賦給臨時(shí)的常量x。類似的腌乡,第二個(gè)case將匹配一個(gè)橫坐標(biāo)為0的點(diǎn),并把這個(gè)點(diǎn)的縱坐標(biāo)賦給臨時(shí)的常量y夜牡。
這個(gè)switch語句不包含默認(rèn)分支与纽。這是因?yàn)樽詈笠粋€(gè)case聲明了一個(gè)可以匹配余下所有值的元組。這使得switch語句已經(jīng)完備了!
這里x和y是常量塘装,這是因?yàn)闆]有必要在其對(duì)應(yīng)的case分支中修改它們的值渣锦。然而,它們也可以是變量氢哮,那么程序?qū)?huì)創(chuàng)建臨時(shí)變量袋毙,并用相應(yīng)的值初始化它。修改這些變量只會(huì)影響其對(duì)應(yī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)建一個(gè)動(dòng)態(tài)的過濾器(filter)皆看。當(dāng)且僅當(dāng)where語句的條件為true時(shí),匹配到的case分支才會(huì)被執(zhí)行背零。
控制轉(zhuǎn)移語句
控制轉(zhuǎn)移語句改變你代碼的執(zhí)行順序腰吟,通過它你可以實(shí)現(xiàn)代碼的跳轉(zhuǎn)。Swift有五種控制轉(zhuǎn)移語句
continue
break
fallthrough
return(“函數(shù)”一節(jié)中介紹)
throw(“錯(cuò)誤拋出”一節(jié)中介紹)
continue
continue語句告訴一個(gè)循環(huán)體立刻停止本次循環(huán)迭代徙瓶,重新開始下次循環(huán)迭代毛雇。
在一個(gè)帶有條件和遞增的for循環(huán)體中,調(diào)用continue語句后侦镇,迭代增量仍然會(huì)被計(jì)算求值灵疮。循環(huán)體繼續(xù)像往常一樣工作,僅僅只是循環(huán)體中的執(zhí)行代碼會(huì)被跳過壳繁。
break
break語句會(huì)立刻結(jié)束整個(gè)控制流的執(zhí)行震捣。當(dāng)你想要更早的結(jié)束一個(gè)switch代碼塊或者一個(gè)循環(huán)體時(shí),你都可以使用break語句闹炉。
循環(huán)語句中的break
當(dāng)在一個(gè)循環(huán)體中使用break時(shí)蒿赢,會(huì)立刻中斷該循環(huán)體的執(zhí)行,然后跳轉(zhuǎn)到表示循環(huán)體結(jié)束的大括號(hào)(})后的第一行代碼渣触。不會(huì)再有本次循環(huán)迭代的代碼被執(zhí)行羡棵,也不會(huì)再有下次的循環(huán)迭代產(chǎn)生。
switch語句中的break
當(dāng)在一個(gè)switch代碼塊中使用break時(shí)昵观,會(huì)立即中斷該switch代碼塊的執(zhí)行晾腔,并且跳轉(zhuǎn)到表示switch代碼塊結(jié)束的大括號(hào)(})后的第一行代碼舌稀。
這種特性可以被用來匹配或者忽略一個(gè)或多個(gè)分支。因?yàn)镾wift的switch需要包含所有的分支而且不允許有為空的分支灼擂,有時(shí)為了使你的意圖更明顯壁查,需要特意匹配或者忽略某個(gè)分支。那么當(dāng)你想忽略某個(gè)分支時(shí)剔应,可以在該分支內(nèi)寫上break語句睡腿。當(dāng)那個(gè)分支被匹配到時(shí),分支內(nèi)的break語句立即結(jié)束switch代碼塊峻贮。
注意:當(dāng)一個(gè)switch分支僅僅包含注釋時(shí)席怪,會(huì)被報(bào)編譯時(shí)錯(cuò)誤。注釋不是代碼語句而且也不能讓switch分支達(dá)到被忽略的效果纤控。你總是可以使用break來忽略某個(gè)分支挂捻。
fallthrough
如果你確實(shí)需要C風(fēng)格的貫穿的特性,你可以在每個(gè)需要該特性的case分支中使用fallthrough`關(guān)鍵字船万。
fallthrough關(guān)鍵字不會(huì)檢查它下一個(gè)將會(huì)落入執(zhí)行的case中的匹配條件刻撒。fallthrough簡(jiǎn)單地使代碼執(zhí)行繼續(xù)連接到下一個(gè)case中的執(zhí)行代碼,這和C語言標(biāo)準(zhǔn)中的switch語句特性是一樣的耿导。
let a =3switcha {case0,1,2:print("0 ~ 2")case3..<100:print("3 ~ 100")// 3 ~ 100fallthroughdefault:print("Over")// Over}
帶標(biāo)簽的語句
在Swift中声怔,你可以在循環(huán)體和switch代碼塊中嵌套循環(huán)體和switch代碼塊來創(chuàng)造復(fù)雜的控制流結(jié)構(gòu)。然而舱呻,循環(huán)體和switch代碼塊兩者都可以使用break語句來提前結(jié)束整個(gè)方法體醋火。因此,顯式地指明break語句想要終止的是哪個(gè)循環(huán)體或者switch代碼塊箱吕,會(huì)很有用芥驳。類似地,如果你有許多嵌套的循環(huán)體殖氏,顯式指明continue語句想要影響哪一個(gè)循環(huán)體也會(huì)非常有用晚树。
可以使用標(biāo)簽來標(biāo)記一個(gè)循環(huán)體或者switch代碼塊,當(dāng)使用break或者continue時(shí)雅采,帶上這個(gè)標(biāo)簽,可以控制該標(biāo)簽代表對(duì)象的中斷或者執(zhí)行慨亲。產(chǎn)生一個(gè)帶標(biāo)簽的語句是通過在該語句的關(guān)鍵詞的同一行前面放置一個(gè)標(biāo)簽婚瓜,并且該標(biāo)簽后面還需帶著一個(gè)冒號(hào)。
/** * 文檔上的蛇和梯子的小游戲刑棵,也叫做滑道和梯子巴刻。 * 現(xiàn)在增加一個(gè)條件:為了獲勝,你必須剛好落在第 25 個(gè)方塊中蛉签。即如果某次擲骰子使你的移動(dòng)超出第 25 個(gè)方塊胡陪,你必須重新擲骰子沥寥,直到你擲出的骰子數(shù)剛好使你能落在第 25 個(gè)方塊中。 */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:// 到達(dá)最后一個(gè)方塊柠座,游戲結(jié)束print(square + diceRoll)breakgameLoopcaselet newSquare where newSquare > finalSquare:// 超出最后一個(gè)方塊邑雅,再擲一次骰子print("square = /(square), diceRoll = /(diceRoll)")continuegameLoopdefault:// 本次移動(dòng)有效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í)行取決于一個(gè)表達(dá)式的布爾值妈经。我們可以使用guard語句來要求條件必須為真時(shí)淮野,以執(zhí)行g(shù)uard語句后的代碼。不同于if語句吹泡,一個(gè)guard語句總是有一個(gè)else分句骤星,如果條件不為真則執(zhí)行else分句中的代碼。
如果guard語句的條件被滿足爆哑,則在保護(hù)語句的封閉大括號(hào)結(jié)束后繼續(xù)執(zhí)行代碼洞难。任何使用了可選綁定作為條件的一部分并被分配了值的變量或常量對(duì)于剩下的保護(hù)語句出現(xiàn)的代碼段是可用的。
如果條件不被滿足揭朝,在else分支上的代碼就會(huì)被執(zhí)行队贱。這個(gè)分支必須轉(zhuǎn)移控制以退出guard語句出現(xiàn)的代碼段。它可以用控制轉(zhuǎn)移語句如return,break,continue或者throw做這件事萝勤,或者調(diào)用一個(gè)不返回的方法或函數(shù)露筒,例如fatalError()。
相比于可以實(shí)現(xiàn)同樣功能的if語句敌卓,按需使用guard語句會(huì)提升我們代碼的可靠性慎式。 它可以使你的代碼連貫的被執(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瘪吏,否則報(bào)上面的錯(cuò)誤? ? }}print("...")
檢測(cè)API可用性
Swift有檢查API可用性的內(nèi)置支持,這可以確保我們不會(huì)不小心地使用對(duì)于當(dāng)前部署目標(biāo)不可用的API蜗巧。
編譯器使用SDK中的可用信息來驗(yàn)證我們的代碼中使用的所有API在項(xiàng)目指定的部署目標(biāo)上是否可用掌眠。如果我們嘗試使用一個(gè)不可用的API,Swift會(huì)在編譯期報(bào)錯(cuò)幕屹。
我們使用一個(gè)可用性條件在一個(gè)if或guard語句中去有條件地執(zhí)行一段代碼蓝丙,這取決于我們想要使用的API是否在運(yùn)行時(shí)是可用的。編譯器使用從可用性條件語句中獲取的信息去驗(yàn)證在代碼塊中調(diào)用的API是否都可用望拖。
if#available(iOS 9, OSX 10.10, *) {print("iOS 9+ or OS X 10.10+")// 條件是或的關(guān)系渺尘,有一個(gè)平臺(tái)滿足就可以,表示指定了在iOS系統(tǒng)上说敏,if段的代碼僅會(huì)在iOS 9及更高版本的系統(tǒng)上執(zhí)行鸥跟;在OS X,僅會(huì)在OS X v10.10及更高版本的系統(tǒng)上執(zhí)行。}else{print("old version")}
可用性條件指定了在iOS系統(tǒng)上医咨,if段的代碼僅會(huì)在iOS 9及更高版本的系統(tǒng)上執(zhí)行枫匾;在OS X,僅會(huì)在OS X v10.10及更高版本的系統(tǒng)上執(zhí)行拟淮。上面的最后一個(gè)參數(shù)*是必須寫的干茉,用于處理未來潛在的平臺(tái)〕颓福可用性條件獲取了一系列平臺(tái)名字和版本等脂。平臺(tái)名字可以是iOS,OSX或watchOS撑蚌。除了特定的主板本號(hào)像iOS 8上遥,我們可以指定較小的版本號(hào)像iOS 8.3以及OS X v10.10.3。