Swift - 控制流

控制流


Swift 提供了多種流程控制結(jié)構(gòu)荔仁,包括可以多次執(zhí)行任務(wù)的 while 循環(huán)伍宦,基于特定條件選擇執(zhí)行不同代碼分支的 ifguardswitch 語句乏梁,還有控制流程跳轉(zhuǎn)到其他代碼位置的 breakcontinue 語句次洼。

Swift 還提供了 for-in 循環(huán),用來更簡單地遍歷數(shù)組(Array)掌呜,字典(Dictionary)滓玖,區(qū)間(Range),字符串(String)和其他序列類型质蕉。

Swift 的 switch 語句比許多類 C 語言要更加強(qiáng)大势篡。case 還可以匹配很多不同的模式,包括范圍匹配模暗,元組(tuple)和特定類型匹配禁悠。switch 語句的 case 中匹配的值可以聲明為臨時常量或變量,在 case 作用域內(nèi)使用兑宇,也可以配合 where 來描述更復(fù)雜的匹配條件碍侦。

For-In 循環(huán)

你可以使用 for-in 循環(huán)來遍歷一個集合中的所有元素,例如數(shù)組中的元素、范圍內(nèi)的數(shù)字或者字符串中的字符瓷产。

以下例子使用 for-in 遍歷一個數(shù)組所有元素:

let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
    print("Hello, \(name)!")
}
// Hello, Anna!
// Hello, Alex!
// Hello, Brian!
// Hello, Jack!

你也可以通過遍歷一個字典來訪問它的鍵值對站玄。遍歷字典時,字典的每項(xiàng)元素會以 (key, value) 元組的形式返回濒旦,你可以在 for-in 循環(huán)中使用顯式的常量名稱來解讀 (key, value) 元組株旷。下面的例子中,字典的鍵會聲明為 animalName 常量尔邓,字典的值會聲明為 legCount 常量:

let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
    print("\(animalName)s have \(legCount) legs")
}
// cats have 4 legs
// ants have 6 legs
// spiders have 8 legs

字典的內(nèi)容理論上是無序的晾剖,遍歷元素時的順序是無法確定的。將元素插入字典的順序并不會決定它們被遍歷的順序梯嗽。關(guān)于數(shù)組和字典的細(xì)節(jié)齿尽,參見 集合類型

for-in 循環(huán)還可以使用數(shù)字范圍灯节。下面的例子用來輸出乘法表的一部分內(nèi)容:

for index in 1...5 {
    print("\(index) times 5 is \(index * 5)")
}
// 1 times 5 is 5
// 2 times 5 is 10
// 3 times 5 is 15
// 4 times 5 is 20
// 5 times 5 is 25

例子中用來進(jìn)行遍歷的元素是使用閉區(qū)間操作符(...)表示的從 15 的數(shù)字區(qū)間循头。index 被賦值為閉區(qū)間中的第一個數(shù)字(1),然后循環(huán)中的語句被執(zhí)行一次炎疆。在本例中贷岸,這個循環(huán)只包含一個語句,用來輸出當(dāng)前 index 值所對應(yīng)的乘 5 乘法表的結(jié)果磷雇。該語句執(zhí)行后偿警,index 的值被更新為閉區(qū)間中的第二個數(shù)字(2),之后 print(_:separator:terminator:) 函數(shù)會再執(zhí)行一次唯笙。整個過程會進(jìn)行到閉區(qū)間結(jié)尾為止螟蒸。

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

如果你不需要區(qū)間序列內(nèi)每一項(xiàng)的值挽放,你可以使用下劃線(_)替代變量名來忽略這個值:

let base = 3
let power = 10
var answer = 1
for _ in 1...power {
    answer *= base
}
print("\(base) to the power of \(power) is \(answer)")
// 輸出“3 to the power of 10 is 59049”

這個例子計(jì)算 base 這個數(shù)的 power 次冪(本例中绍赛,是 310 次冪),從 130 次冪)開始做 3 的乘法辑畦, 進(jìn)行 10 次吗蚌,使用 110 的閉區(qū)間循環(huán)。這個計(jì)算并不需要知道每一次循環(huán)中計(jì)數(shù)器具體的值纯出,只需要執(zhí)行了正確的循環(huán)次數(shù)即可蚯妇。下劃線符號 _ (替代循環(huán)中的變量)能夠忽略當(dāng)前值敷燎,并且不提供循環(huán)遍歷時對值的訪問。

在某些情況下箩言,你可能不想使用包括兩個端點(diǎn)的閉區(qū)間硬贯。想象一下,你在一個手表上繪制分鐘的刻度線陨收〕纬桑總共 60 個刻度,從 0 分開始畏吓。使用半開區(qū)間運(yùn)算符(..<)來表示一個左閉右開的區(qū)間。有關(guān)區(qū)間的更多信息卫漫,請參閱 區(qū)間運(yùn)算符菲饼。

let minutes = 60
for tickMark in 0..<minutes {
    // 每一分鐘都渲染一個刻度線(60次)
}

一些用戶可能在其 UI 中可能需要較少的刻度。他們可以每 5 分鐘作為一個刻度列赎。使用 stride(from:to:by:) 函數(shù)跳過不需要的標(biāo)記宏悦。

let minuteInterval = 5
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
    // 每5分鐘渲染一個刻度線(0, 5, 10, 15 ... 45, 50, 55)
}

可以在閉區(qū)間使用 stride(from:through:by:) 起到同樣作用:

let hours = 12
let hourInterval = 3
for tickMark in stride(from: 3, through: hours, by: hourInterval) {
    // 每3小時渲染一個刻度線(3, 6, 9, 12)
}

以上示例使用 for-in 循環(huán)來遍歷范圍、數(shù)組包吝、字典和字符串饼煞。你可以用它來遍歷任何的集合,包括實(shí)現(xiàn)了 Sequence 協(xié)議的自定義類或集合類型诗越。

While 循環(huán)

while 循環(huán)會一直運(yùn)行一段語句直到條件變成 false砖瞧。這類循環(huán)適合使用在第一次迭代前,迭代次數(shù)未知的情況下嚷狞。Swift 提供兩種 while 循環(huán)形式:

  • while 循環(huán)块促,每次在循環(huán)開始時計(jì)算條件是否符合;

  • repeat-while 循環(huán)床未,每次在循環(huán)結(jié)束時計(jì)算條件是否符合竭翠。

While

while 循環(huán)從計(jì)算一個條件開始。如果條件為 true薇搁,會重復(fù)運(yùn)行一段語句斋扰,直到條件變?yōu)?false

下面是 while 循環(huán)的一般格式:

while condition {
    statements
}

下面的例子來玩一個叫做蛇和梯子(也叫做滑道和梯子)的小游戲:

image.png

游戲的規(guī)則如下:

  • 游戲盤面包括 25 個方格啃洋,游戲目標(biāo)是達(dá)到或者超過第 25 個方格传货;

  • 每一輪,你通過擲一個六面體骰子來確定你移動方塊的步數(shù)宏娄,移動的路線由上圖中橫向的虛線所示损离;

  • 如果在某輪結(jié)束,你移動到了梯子的底部绝编,可以順著梯子爬上去僻澎;

  • 如果在某輪結(jié)束貌踏,你移動到了蛇的頭部,你會順著蛇的身體滑下去窟勃。

游戲盤面可以使用一個 Int 數(shù)組來表達(dá)祖乳。數(shù)組的長度由一個 finalSquare 常量儲存,用來初始化數(shù)組和檢測最終勝利條件秉氧。游戲盤面由 26 個 Int 0 值初始化眷昆,而不是 25 個(由 025,一共 26 個):

let finalSquare = 25
var board = [Int](repeating: 0, count: finalSquare + 1)

一些方格被設(shè)置成特定的值來表示有蛇或者梯子汁咏。梯子底部的方格是一個正值亚斋,使你可以向上移動,蛇頭處的方格是一個負(fù)值攘滩,會讓你向下移動:

board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08

3 號方格是梯子的底部帅刊,會讓你向上移動到 11 號方格,我們使用 board[03] 等于 +08(來表示 113 之間的差值)漂问。為了對齊語句赖瞒,這里使用了一元正運(yùn)算符(+i)和一元負(fù)運(yùn)算符(-i),并且小于 10 的數(shù)字都使用 0 補(bǔ)齊(這些語法的技巧不是必要的蚤假,只是為了讓代碼看起來更加整潔)栏饮。

玩家由左下角空白處編號為 0 的方格開始游戲。玩家第一次擲骰子后才會進(jìn)入游戲盤面:

var square = 0
var diceRoll = 0
while square < finalSquare {
    // 擲骰子
    diceRoll +=  1
    if diceRoll == 7 { diceRoll = 1 }
    // 根據(jù)點(diǎn)數(shù)移動
    square += diceRoll
    if square < board.count {
        // 如果玩家還在棋盤上磷仰,順著梯子爬上去或者順著蛇滑下去
        square += board[square]
    }
}
print("Game over!")

本例中使用了最簡單的方法來模擬擲骰子袍嬉。diceRoll 的值并不是一個隨機(jī)數(shù),而是以 0 為初始值灶平,之后每一次 while 循環(huán)冬竟,diceRoll 的值增加 1 ,然后檢測是否超出了最大值民逼。當(dāng) diceRoll 的值等于 7 時泵殴,就超過了骰子的最大值,會被重置為 1拼苍。所以 diceRoll 的取值順序會一直是 1笑诅,23疮鲫,4吆你,56俊犯,1妇多,2 等。

擲完骰子后燕侠,玩家向前移動 diceRoll 個方格者祖,如果玩家移動超過了第 25 個方格立莉,這個時候游戲?qū)Y(jié)束贞盯,為了應(yīng)對這種情況亲桦,代碼會首先判斷 square 的值是否小于 boardcount 屬性宽堆,只有小于才會在 board[square] 上增加 square血巍,來向前或向后移動(遇到了梯子或者蛇)。

注意

如果沒有這個檢測(square < board.count)恋日,board[square] 可能會越界訪問 board 數(shù)組祟偷,導(dǎo)致運(yùn)行時錯誤芹橡。

當(dāng)本輪 while 循環(huán)運(yùn)行完畢讥耗,會再檢測循環(huán)條件是否需要再運(yùn)行一次循環(huán)有勾。如果玩家移動到或者超過第 25 個方格,循環(huán)條件結(jié)果為 false古程,此時游戲結(jié)束蔼卡。

while 循環(huán)比較適合本例中的這種情況,因?yàn)樵?while 循環(huán)開始時籍琳,我們并不知道游戲要跑多久,只有在達(dá)成指定條件時循環(huán)才會結(jié)束贷祈。

Repeat-While

while 循環(huán)的另外一種形式是 repeat-while趋急,它和 while 的區(qū)別是在判斷循環(huán)條件之前,先執(zhí)行一次循環(huán)的代碼塊势誊。然后重復(fù)循環(huán)直到條件為 false呜达。

注意

Swift 語言的 repeat-while 循環(huán)和其他語言中的 do-while 循環(huán)是類似的。

下面是 repeat-while 循環(huán)的一般格式:

repeat {
    statements
} while condition

還是蛇和梯子的游戲粟耻,使用 repeat-while 循環(huán)來替代 while 循環(huán)查近。finalSquareboard挤忙、squarediceRoll 的值初始化同 while 循環(huán)時一樣:

let finalSquare = 25
var board = [Int](repeating: 0, count: finalSquare + 1)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
var square = 0
var diceRoll = 0

repeat-while 的循環(huán)版本霜威,循環(huán)中第一步就需要去檢測是否在梯子或者蛇的方塊上。沒有梯子會讓玩家直接上到第 25 個方格册烈,所以玩家不會通過梯子直接贏得游戲戈泼。這樣在循環(huán)開始時先檢測是否踩在梯子或者蛇上是安全的。

游戲開始時赏僧,玩家在第 0 個方格上大猛,board[0] 一直等于 0, 不會有什么影響:

repeat {
    // 順著梯子爬上去或者順著蛇滑下去
    square += board[square]
    // 擲骰子
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }
    // 根據(jù)點(diǎn)數(shù)移動
    square += diceRoll
} while square < finalSquare
print("Game over!")

檢測完玩家是否踩在梯子或者蛇上之后淀零,開始擲骰子挽绩,然后玩家向前移動 diceRoll 個方格,本輪循環(huán)結(jié)束驾中。

循環(huán)條件(while square < finalSquare)和 while 方式相同唉堪,但是只會在循環(huán)結(jié)束后進(jìn)行計(jì)算模聋。在這個游戲中,repeat-while 表現(xiàn)得比 while 循環(huán)更好巨坊。repeat-while 方式會在條件判斷 square 沒有超出后直接運(yùn)行 square += board[square]撬槽,這種方式可以比起前面 while 循環(huán)的版本,可以省去數(shù)組越界的檢查趾撵。

條件語句

根據(jù)特定的條件執(zhí)行特定的代碼通常是十分有用的侄柔。當(dāng)錯誤發(fā)生時,你可能想運(yùn)行額外的代碼占调;或者暂题,當(dāng)值太大或太小時,向用戶顯示一條消息究珊。要實(shí)現(xiàn)這些功能薪者,你就需要使用條件語句

Swift 提供兩種類型的條件語句:if 語句和 switch 語句剿涮。通常言津,當(dāng)條件較為簡單且可能的情況很少時,使用 if 語句取试。而 switch 語句更適用于條件較復(fù)雜悬槽、有更多排列組合的時候。并且 switch 在需要用到模式匹配(pattern-matching)的情況下會更有用瞬浓。

If

if 語句最簡單的形式就是只包含一個條件初婆,只有該條件為 true 時,才執(zhí)行相關(guān)代碼:

var temperatureInFahrenheit = 30
if temperatureInFahrenheit <= 32 {
    print("It's very cold. Consider wearing a scarf.")
}
// 輸出“It's very cold. Consider wearing a scarf.”

上面的例子會判斷溫度是否小于等于 32 華氏度(水的冰點(diǎn))猿棉。如果是磅叛,則打印一條消息;否則萨赁,不打印任何消息弊琴,繼續(xù)執(zhí)行 if 塊后面的代碼。

當(dāng)然杖爽,if 語句允許二選一執(zhí)行访雪,叫做 else 從句。也就是當(dāng)條件為 false 時掂林,執(zhí)行 else 語句

temperatureInFahrenheit = 40
if temperatureInFahrenheit <= 32 {
    print("It's very cold. Consider wearing a scarf.")
} else {
    print("It's not that cold. Wear a t-shirt.")
}
// 輸出“It's not that cold. Wear a t-shirt.”

顯然臣缀,這兩條分支中總有一條會被執(zhí)行。由于溫度已升至 40 華氏度泻帮,不算太冷精置,沒必要再圍圍巾。因此锣杂,else 分支就被觸發(fā)了脂倦。

你可以把多個 if 語句鏈接在一起番宁,來實(shí)現(xiàn)更多分支:

temperatureInFahrenheit = 90
if temperatureInFahrenheit <= 32 {
    print("It's very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 {
    print("It's really warm. Don't forget to wear sunscreen.")
} else {
    print("It's not that cold. Wear a t-shirt.")
}
// 輸出“It's really warm. Don't forget to wear sunscreen.”

在上面的例子中,額外的 if 語句用于判斷是不是特別熱赖阻。而最后的 else 語句被保留了下來蝶押,用于打印既不冷也不熱時的消息。

實(shí)際上火欧,當(dāng)不需要完整判斷情況的時候棋电,最后的 else 語句是可選的:

temperatureInFahrenheit = 72
if temperatureInFahrenheit <= 32 {
    print("It's very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 {
    print("It's really warm. Don't forget to wear sunscreen.")
}

在這個例子中,由于既不冷也不熱苇侵,所以不會觸發(fā) ifelse if 分支赶盔,也就不會打印任何消息。

Switch

switch 語句會嘗試把某個值與若干個模式(pattern)進(jìn)行匹配榆浓。根據(jù)第一個匹配成功的模式于未,switch 語句會執(zhí)行對應(yīng)的代碼。當(dāng)有可能的情況較多時陡鹃,通常用 switch 語句替換 if 語句烘浦。

switch 語句最簡單的形式就是把某個值與一個或若干個相同類型的值作比較:

switch some value to consider {
case value 1:
    respond to value 1
case value 2,
    value 3:
    respond to value 2 or 3
default:
    otherwise, do something else
}

switch 語句由多個 case 構(gòu)成,每個由 case 關(guān)鍵字開始萍鲸。為了匹配某些更特定的值闷叉,Swift 提供了幾種方法來進(jìn)行更復(fù)雜的模式匹配,這些模式將在本節(jié)的稍后部分提到猿推。

if 語句類似片习,每一個 case 都是代碼執(zhí)行的一條分支捌肴。switch 語句會決定哪一條分支應(yīng)該被執(zhí)行蹬叭,這個流程被稱作根據(jù)給定的值切換(switching)

switch 語句必須是完備的状知。這就是說秽五,每一個可能的值都必須至少有一個 case 分支與之對應(yīng)。在某些不可能涵蓋所有值的情況下饥悴,你可以使用默認(rèn)(default)分支來涵蓋其它所有沒有對應(yīng)的值坦喘,這個默認(rèn)分支必須在 switch 語句的最后面。

下面的例子使用 switch 語句來匹配一個名為 someCharacter 的小寫字符:

let someCharacter: Character = "z"
switch someCharacter {
case "a":
    print("The first letter of the alphabet")
case "z":
    print("The last letter of the alphabet")
default:
    print("Some other character")
}
// 輸出“The last letter of the alphabet”

在這個例子中西设,第一個 case 分支用于匹配第一個英文字母 a瓣铣,第二個 case 分支用于匹配最后一個字母 z。因?yàn)?switch 語句必須有一個 case 分支用于覆蓋所有可能的字符贷揽,而不僅僅是所有的英文字母棠笑,所以 switch 語句使用 default 分支來匹配除了 az 外的所有值,這個分支保證了 switch 語句的完備性禽绪。

不存在隱式的貫穿

與 C 和 Objective-C 中的 switch 語句不同蓖救,在 Swift 中洪规,當(dāng)匹配的 case 分支中的代碼執(zhí)行完畢后,程序會終止 switch 語句循捺,而不會繼續(xù)執(zhí)行下一個 case 分支斩例。這也就是說,不需要在 case 分支中顯式地使用 break 語句从橘。這使得 switch 語句更安全念赶、更易用,也避免了漏寫 break 語句導(dǎo)致多個語言被執(zhí)行的錯誤洋满。

注意

雖然在 Swift 中 break 不是必須的晶乔,但你依然可以在 case 分支中的代碼執(zhí)行完畢前使用 break 跳出,詳情請參見 Switch 語句中的 break牺勾。

每一個 case 分支都必須包含至少一條語句正罢。像下面這樣書寫代碼是無效的,因?yàn)榈谝粋€ case 分支是空的:

let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a": // 無效驻民,這個分支下面沒有語句
case "A":
    print("The letter A")
default:
    print("Not the letter A")
}
// 這段代碼會報(bào)編譯錯誤

不像 C 語言里的 switch 語句翻具,在 Swift 中,switch 語句不會一起匹配 "a""A"回还。相反的裆泳,上面的代碼會引起編譯期錯誤:case "a": 不包含任何可執(zhí)行語句——這就避免了意外地從一個 case 分支貫穿到另外一個,使得代碼更安全柠硕、也更直觀工禾。

為了讓單個 case 同時匹配 aA,可以將這個兩個值組合成一個復(fù)合匹配蝗柔,并且用逗號分開:

let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":
    print("The letter A")
default:
    print("Not the letter A")
}
// 輸出“The letter A”

為了可讀性闻葵,符合匹配可以寫成多行形式,詳情請參考稍后講解的復(fù)合匹配癣丧。

注意

如果想要顯式貫穿 case 分支槽畔,請使用 fallthrough 語句,詳情請參考 貫穿胁编。

區(qū)間匹配

case 分支的模式也可以是一個值的區(qū)間厢钧。下面的例子展示了如何使用區(qū)間匹配來輸出任意數(shù)字對應(yīng)的自然語言格式:

let approximateCount = 62
let countedThings = "moons orbiting Saturn"
let naturalCount: String
switch approximateCount {
case 0:
    naturalCount = "no"
case 1..<5:
    naturalCount = "a few"
case 5..<12:
    naturalCount = "several"
case 12..<100:
    naturalCount = "dozens of"
case 100..<1000:
    naturalCount = "hundreds of"
default:
    naturalCount = "many"
}
print("There are \(naturalCount) \(countedThings).")
// 輸出“There are dozens of moons orbiting Saturn.”

在上例中,approximateCount 在一個 switch 聲明中被評估嬉橙。每一個 case 都與之進(jìn)行比較早直。因?yàn)?approximateCount 落在了 12 到 100 的區(qū)間,所以 naturalCount 等于 "dozens of" 值市框,并且此后的執(zhí)行跳出了 switch 語句霞扬。

元組

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

下面的例子展示了如何使用一個 (Int, Int) 類型的元組來分類下圖中的點(diǎn) (x, y):

let somePoint = (1, 1)
switch somePoint {
case (0, 0):
    print("\(somePoint) is at the origin")
case (_, 0):
    print("\(somePoint) is on the x-axis")
case (0, _):
    print("\(somePoint) is on the y-axis")
case (-2...2, -2...2):
    print("\(somePoint) is inside the box")
default:
    print("\(somePoint) is outside of the box")
}
// 輸出“(1, 1) is inside the box”
image.png

在上面的例子中级及,switch 語句會判斷某個點(diǎn)是否是原點(diǎn) (0, 0)乒疏,是否在紅色的 x 軸上,是否在橘黃色的 y 軸上饮焦,是否在一個以原點(diǎn)為中心的4x4的藍(lán)色矩形里怕吴,或者在這個矩形外面。

不像 C 語言县踢,Swift 允許多個 case 匹配同一個值转绷。實(shí)際上,在這個例子中硼啤,點(diǎn) (0, 0)可以匹配所有四個 case议经。但是,如果存在多個匹配谴返,那么只會執(zhí)行第一個被匹配到的 case 分支煞肾。考慮點(diǎn) (0, 0)會首先匹配 case (0, 0)嗓袱,因此剩下的能夠匹配的分支都會被忽視掉籍救。

值綁定(Value Bindings)

case 分支允許將匹配的值聲明為臨時常量或變量,并且在 case 分支體內(nèi)使用 —— 這種行為被稱為值綁定(value binding)渠抹,因?yàn)槠ヅ涞闹翟?case 分支體內(nèi)蝙昙,與臨時的常量或變量綁定。

下面的例子將下圖中的點(diǎn) (x, y)梧却,使用 (Int, Int) 類型的元組表示奇颠,然后分類表示:

let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
    print("on the x-axis with an x value of \(x)")
case (0, let y):
    print("on the y-axis with a y value of \(y)")
case let (x, y):
    print("somewhere else at (\(x), \(y))")
}
// 輸出“on the x-axis with an x value of 2”
image.png

在上面的例子中,switch 語句會判斷某個點(diǎn)是否在紅色的 x 軸上篮幢,是否在橘黃色的 y 軸上大刊,或者不在坐標(biāo)軸上为迈。

這三個 case 都聲明了常量 xy 的占位符三椿,用于臨時獲取元組 anotherPoint 的一個或兩個值。第一個 case ——case (let x, 0) 將匹配一個縱坐標(biāo)為 0 的點(diǎn)葫辐,并把這個點(diǎn)的橫坐標(biāo)賦給臨時的常量 x搜锰。類似的,第二個 case ——case (0, let y) 將匹配一個橫坐標(biāo)為 0 的點(diǎn)耿战,并把這個點(diǎn)的縱坐標(biāo)賦給臨時的常量 y蛋叼。

一旦聲明了這些臨時的常量,它們就可以在其對應(yīng)的 case 分支里使用。在這個例子中狈涮,它們用于打印給定點(diǎn)的類型狐胎。

請注意,這個 switch 語句不包含默認(rèn)分支歌馍。這是因?yàn)樽詈笠粋€ case ——case let(x, y) 聲明了一個可以匹配余下所有值的元組握巢。這使得 switch 語句已經(jīng)完備了,因此不需要再書寫默認(rèn)分支松却。

Where

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

下面的例子把下圖中的點(diǎn) (x, y)進(jìn)行了分類:

let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
    print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
    print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
    print("(\(x), \(y)) is just some arbitrary point")
}
// 輸出“(1, -1) is on the line x == -y”
image.png

在上面的例子中,switch 語句會判斷某個點(diǎn)是否在綠色的對角線 x == y 上晓锻,是否在紫色的對角線 x == -y 上歌焦,或者不在對角線上。

這三個 case 都聲明了常量 xy 的占位符砚哆,用于臨時獲取元組 yetAnotherPoint 的兩個值独撇。這兩個常量被用作 where 語句的一部分,從而創(chuàng)建一個動態(tài)的過濾器(filter)躁锁。當(dāng)且僅當(dāng) where 語句的條件為 true 時券勺,匹配到的 case 分支才會被執(zhí)行。

就像是值綁定中的例子灿里,由于最后一個 case 分支匹配了余下所有可能的值关炼,switch 語句就已經(jīng)完備了,因此不需要再書寫默認(rèn)分支匣吊。

復(fù)合型 Cases

當(dāng)多個條件可以使用同一種方法來處理時儒拂,可以將這幾種可能放在同一個 case 后面,并且用逗號隔開色鸳。當(dāng) case 后面的任意一種模式匹配的時候社痛,這條分支就會被匹配。并且命雀,如果匹配列表過長蒜哀,還可以分行書寫:

let someCharacter: Character = "e"
switch someCharacter {
case "a", "e", "i", "o", "u":
    print("\(someCharacter) is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
     "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
    print("\(someCharacter) is a consonant")
default:
    print("\(someCharacter) is not a vowel or a consonant")
}
// 輸出“e is a vowel”

這個 switch 語句中的第一個 case,匹配了英語中的五個小寫元音字母吏砂。相似的撵儿,第二個 case 匹配了英語中所有的小寫輔音字母。最終狐血,default 分支匹配了其它所有字符淀歇。

復(fù)合匹配同樣可以包含值綁定。復(fù)合匹配里所有的匹配模式匈织,都必須包含相同的值綁定浪默。并且每一個綁定都必須獲取到相同類型的值牡直。這保證了,無論復(fù)合匹配中的哪個模式發(fā)生了匹配纳决,分支體內(nèi)的代碼碰逸,都能獲取到綁定的值,并且綁定的值都有一樣的類型阔加。

let stillAnotherPoint = (9, 0)
switch stillAnotherPoint {
case (let distance, 0), (0, let distance):
    print("On an axis, \(distance) from the origin")
default:
    print("Not on an axis")
}
// 輸出“On an axis, 9 from the origin”

上面的 case 有兩個模式:(let distance, 0) 匹配了在 x 軸上的值花竞,(0, let distance) 匹配了在 y 軸上的值。兩個模式都綁定了 distance掸哑,并且 distance 在兩種模式下约急,都是整型——這意味著分支體內(nèi)的代碼,只要 case 匹配苗分,都可以獲取到 distance 值厌蔽。

控制轉(zhuǎn)移語句

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

  • continue

  • break

  • fallthrough

  • return

  • throw

我們將會在下面討論 continue奴饮、breakfallthrough 語句。return 語句將會在 函數(shù) 章節(jié)討論择浊,throw 語句會在 錯誤拋出 章節(jié)討論戴卜。

Continue

continue 語句告訴一個循環(huán)體立刻停止本次循環(huán),重新開始下次循環(huán)琢岩。就好像在說“本次循環(huán)我已經(jīng)執(zhí)行完了”投剥,但是并不會離開整個循環(huán)體。

下面的例子把一個小寫字符串中的元音字母和空格字符移除担孔,生成了一個含義模糊的短句:

let puzzleInput = "great minds think alike"
var puzzleOutput = ""
for character in puzzleInput {
    switch character {
    case "a", "e", "i", "o", "u", " ":
        continue
    default:
        puzzleOutput.append(character)
    }
}
print(puzzleOutput)
    // 輸出“grtmndsthnklk”

在上面的代碼中江锨,只要匹配到元音字母或者空格字符,就調(diào)用 continue 語句糕篇,使本次循環(huán)結(jié)束啄育,重新開始下次循環(huán)。這種行為使 switch 匹配到元音字母和空格字符時不做處理拌消,而不是讓每一個匹配到的字符都被打印挑豌。

Break

break 語句會立刻結(jié)束整個控制流的執(zhí)行。break 可以在 switch 或循環(huán)語句中使用墩崩,用來提前結(jié)束 switch 或循環(huán)語句氓英。

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

當(dāng)在一個循環(huán)體中使用 break 時,會立刻中斷該循環(huán)體的執(zhí)行泰鸡,然后跳轉(zhuǎn)到表示循環(huán)體結(jié)束的大括號(})后的第一行代碼债蓝。不會再有本次循環(huán)的代碼被執(zhí)行壳鹤,也不會再有下次的循環(huán)產(chǎn)生盛龄。

Switch 語句中的 break

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

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

注意

當(dāng)一個 switch 分支僅僅包含注釋時绊谭,會被報(bào)編譯時錯誤政恍。注釋不是代碼語句而且也不能讓 switch 分支達(dá)到被忽略的效果。你應(yīng)該使用 break 來忽略某個分支达传。

下面的例子通過 switch 來判斷一個 Character 值是否代表下面四種語言之一篙耗。為了簡潔,多個值被包含在了同一個分支情況中宪赶。

let numberSymbol: Character = "三"  // 簡體中文里的數(shù)字 3
var possibleIntegerValue: Int?
switch numberSymbol {
case "1", "?", "一", "?":
    possibleIntegerValue = 1
case "2", "?", "二", "?":
    possibleIntegerValue = 2
case "3", "?", "三", "?":
    possibleIntegerValue = 3
case "4", "?", "四", "?":
    possibleIntegerValue = 4
default:
    break
}
if let integerValue = possibleIntegerValue {
    print("The integer value of \(numberSymbol) is \(integerValue).")
} else {
    print("An integer value could not be found for \(numberSymbol).")
}
// 輸出“The integer value of 三 is 3.”

這個例子檢查 numberSymbol 是否是拉丁宗弯,阿拉伯,中文或者泰語中的 14 之一搂妻。如果被匹配到蒙保,該 switch 分支語句給 Int? 類型變量 possibleIntegerValue 設(shè)置一個整數(shù)值。

當(dāng) switch 代碼塊執(zhí)行完后欲主,接下來的代碼通過使用可選綁定來判斷 possibleIntegerValue 是否曾經(jīng)被設(shè)置過值追他。因?yàn)槭强蛇x類型的緣故,possibleIntegerValue 有一個隱式的初始值 nil岛蚤,所以僅僅當(dāng) possibleIntegerValue 曾被 switch 代碼塊的前四個分支中的某個設(shè)置過一個值時邑狸,可選的綁定才會被判定為成功。

在上面的例子中涤妒,想要把 Character 所有的的可能性都枚舉出來是不現(xiàn)實(shí)的单雾,所以使用 default 分支來包含所有上面沒有匹配到字符的情況。由于這個 default 分支不需要執(zhí)行任何動作她紫,所以它只寫了一條 break 語句硅堆。一旦落入到 default 分支中后,break 語句就完成了該分支的所有代碼操作贿讹,代碼繼續(xù)向下渐逃,開始執(zhí)行 if let 語句。

貫穿(Fallthrough)

在 Swift 里民褂,switch 語句不會從上一個 case 分支跳轉(zhuǎn)到下一個 case 分支中茄菊。相反疯潭,只要第一個匹配到的 case 分支完成了它需要執(zhí)行的語句,整個 switch 代碼塊完成了它的執(zhí)行面殖。相比之下竖哩,C 語言要求你顯式地插入 break 語句到每個 case 分支的末尾來阻止自動落入到下一個 case 分支中。Swift 的這種避免默認(rèn)落入到下一個分支中的特性意味著它的 switch 功能要比 C 語言的更加清晰和可預(yù)測脊僚,可以避免無意識地執(zhí)行多個 case 分支從而引發(fā)的錯誤相叁。

如果你確實(shí)需要 C 風(fēng)格的貫穿的特性,你可以在每個需要該特性的 case 分支中使用 fallthrough 關(guān)鍵字辽幌。下面的例子使用 fallthrough 來創(chuàng)建一個數(shù)字的描述語句增淹。

let integerToDescribe = 5
var description = "The number \(integerToDescribe) is"
switch integerToDescribe {
case 2, 3, 5, 7, 11, 13, 17, 19:
    description += " a prime number, and also"
    fallthrough
default:
    description += " an integer."
}
print(description)
// 輸出“The number 5 is a prime number, and also an integer.”

這個例子定義了一個 String 類型的變量 description 并且給它設(shè)置了一個初始值。函數(shù)使用 switch 邏輯來判斷 integerToDescribe 變量的值乌企。當(dāng) integerToDescribe 的值屬于列表中的質(zhì)數(shù)之一時埠通,該函數(shù)在 description 后添加一段文字,來表明這個數(shù)字是一個質(zhì)數(shù)逛犹。然后它使用 fallthrough 關(guān)鍵字來“貫穿”到 default 分支中端辱。default 分支在 description 的最后添加一段額外的文字,至此 switch 代碼塊執(zhí)行完了虽画。

如果 integerToDescribe 的值不屬于列表中的任何質(zhì)數(shù)舞蔽,那么它不會匹配到第一個 switch 分支。而這里沒有其他特別的分支情況码撰,所以 integerToDescribe 匹配到 default 分支中渗柿。

當(dāng) switch 代碼塊執(zhí)行完后,使用 print(_:separator:terminator:) 函數(shù)打印該數(shù)字的描述脖岛。在這個例子中朵栖,數(shù)字 5 被準(zhǔn)確的識別為了一個質(zhì)數(shù)。

注意

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

帶標(biāo)簽的語句

在 Swift 中绍在,你可以在循環(huán)體和條件語句中嵌套循環(huán)體和條件語句來創(chuàng)造復(fù)雜的控制流結(jié)構(gòu)门扇。并且,循環(huán)體和條件語句都可以使用 break 語句來提前結(jié)束整個代碼塊偿渡。因此臼寄,顯式地指明 break 語句想要終止的是哪個循環(huán)體或者條件語句,會很有用溜宽。類似地吉拳,如果你有許多嵌套的循環(huán)體,顯式指明 continue 語句想要影響哪一個循環(huán)體也會非常有用适揉。

為了實(shí)現(xiàn)這個目的留攒,你可以使用標(biāo)簽(statement label)來標(biāo)記一個循環(huán)體或者條件語句煤惩,對于一個條件語句,你可以使用 break 加標(biāo)簽的方式稼跳,來結(jié)束這個被標(biāo)記的語句盟庞。對于一個循環(huán)語句吃沪,你可以使用 break 或者 continue 加標(biāo)簽汤善,來結(jié)束或者繼續(xù)這條被標(biāo)記語句的執(zhí)行。

聲明一個帶標(biāo)簽的語句是通過在該語句的關(guān)鍵詞的同一行前面放置一個標(biāo)簽票彪,作為這個語句的前導(dǎo)關(guān)鍵字(introducer keyword)红淡,并且該標(biāo)簽后面跟隨一個冒號。下面是一個針對 while 循環(huán)體的標(biāo)簽語法降铸,同樣的規(guī)則適用于所有的循環(huán)體和條件語句在旱。

 label name: while condition {
     statements
 }

下面的例子是前面章節(jié)中蛇和梯子的適配版本,在此版本中推掸,我們將使用一個帶有標(biāo)簽的 while 循環(huán)體中調(diào)用 breakcontinue 語句桶蝎。這次,游戲增加了一條額外的規(guī)則:

  • 為了獲勝谅畅,你必須剛好落在第 25 個方塊中登渣。

如果某次擲骰子使你的移動超出第 25 個方塊,你必須重新擲骰子毡泻,直到你擲出的骰子數(shù)剛好使你能落在第 25 個方塊中胜茧。

游戲的棋盤和之前一樣:

image.png

finalSquareboard仇味、squarediceRoll 值被和之前一樣的方式初始化:

let finalSquare = 25
var board = [Int](repeating: 0, count: finalSquare + 1)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
var square = 0
var diceRoll = 0

這個版本的游戲使用 while 循環(huán)和 switch 語句來實(shí)現(xiàn)游戲的邏輯呻顽。while 循環(huán)有一個標(biāo)簽名 gameLoop,來表明它是游戲的主循環(huán)丹墨。

while 循環(huán)體的條件判斷語句是 while square != finalSquare廊遍,這表明你必須剛好落在方格25中。

gameLoop: while square != finalSquare {
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }
    switch square + diceRoll {
    case finalSquare:
        // 骰子數(shù)剛好使玩家移動到最終的方格里贩挣,游戲結(jié)束昧碉。
        break gameLoop
    case let newSquare where newSquare > finalSquare:
        // 骰子數(shù)將會使玩家的移動超出最后的方格,那么這種移動是不合法的揽惹,玩家需要重新擲骰子
        continue gameLoop
    default:
        // 合法移動被饿,做正常的處理
        square += diceRoll
        square += board[square]
    }
}
print("Game over!")

每次循環(huán)迭代開始時擲骰子。與之前玩家擲完骰子就立即移動不同搪搏,這里使用了 switch 語句來考慮每次移動可能產(chǎn)生的結(jié)果狭握,從而決定玩家本次是否能夠移動。

  • 如果骰子數(shù)剛好使玩家移動到最終的方格里疯溺,游戲結(jié)束论颅。break gameLoop 語句跳轉(zhuǎn)控制去執(zhí)行 while 循環(huán)體后的第一行代碼哎垦,意味著游戲結(jié)束。

  • 如果骰子數(shù)將會使玩家的移動超出最后的方格恃疯,那么這種移動是不合法的漏设,玩家需要重新擲骰子。continue gameLoop 語句結(jié)束本次 while 循環(huán)今妄,開始下一次循環(huán)郑口。

  • 在剩余的所有情況中,骰子數(shù)產(chǎn)生的都是合法的移動盾鳞。玩家向前移動 diceRoll 個方格犬性,然后游戲邏輯再處理玩家當(dāng)前是否處于蛇頭或者梯子的底部。接著本次循環(huán)結(jié)束腾仅,控制跳轉(zhuǎn)到 while 循環(huán)體的條件判斷語句處乒裆,再決定是否需要繼續(xù)執(zhí)行下次循環(huán)。

注意

如果上述的 break 語句沒有使用 gameLoop 標(biāo)簽推励,那么它將會中斷 switch 語句而不是 while 循環(huán)鹤耍。使用 gameLoop 標(biāo)簽清晰的表明了 break 想要中斷的是哪個代碼塊。

同時請注意验辞,當(dāng)調(diào)用 continue gameLoop 去跳轉(zhuǎn)到下一次循環(huán)迭代時稿黄,這里使用 gameLoop 標(biāo)簽并不是嚴(yán)格必須的。因?yàn)樵谶@個游戲中受神,只有一個循環(huán)體抛猖,所以 continue 語句會影響到哪個循環(huán)體是沒有歧義的。然而鼻听,continue 語句使用 gameLoop 標(biāo)簽也是沒有危害的财著。這樣做符合標(biāo)簽的使用規(guī)則,同時參照旁邊的 break gameLoop撑碴,能夠使游戲的邏輯更加清晰和易于理解撑教。

提前退出

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

func greet(person: [String: String]) {
    guard let name = person["name"] else {
        return
    }

    print("Hello \(name)!")

    guard let location = person["location"] else {
        print("I hope the weather is nice near you.")
        return
    }

    print("I hope the weather is nice in \(location).")
}

greet(person: ["name": "John"])
// 輸出“Hello John!”
// 輸出“I hope the weather is nice near you.”
greet(person: ["name": "Jane", "location": "Cupertino"])
// 輸出“Hello Jane!”
// 輸出“I hope the weather is nice in Cupertino.”

如果 guard 語句的條件被滿足排吴,則繼續(xù)執(zhí)行 guard 語句大括號后的代碼秆乳。將變量或者常量的可選綁定作為 guard 語句的條件,都可以保護(hù) guard 語句后面的代碼。

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

相比于可以實(shí)現(xiàn)同樣功能的 if 語句,按需使用 guard 語句會提升我們代碼的可讀性嘶摊。它可以使你的代碼連貫的被執(zhí)行而不需要將它包在 else 塊中延蟹,它可以使你在緊鄰條件判斷的地方评矩,處理違規(guī)的情況叶堆。

檢測 API 可用性

Swift 內(nèi)置支持檢查 API 可用性,這可以確保我們不會在當(dāng)前部署機(jī)器上斥杜,不小心地使用了不可用的 API虱颗。

編譯器使用 SDK 中的可用信息來驗(yàn)證我們的代碼中使用的所有 API 在項(xiàng)目指定的部署目標(biāo)上是否可用。如果我們嘗試使用一個不可用的 API蔗喂,Swift 會在編譯時報(bào)錯。

我們在 ifguard 語句中使用 可用性條件(availability condition)去有條件的執(zhí)行一段代碼,來在運(yùn)行時判斷調(diào)用的 API 是否可用舔稀。編譯器使用從可用性條件語句中獲取的信息去驗(yàn)證露泊,在這個代碼塊中調(diào)用的 API 是否可用。

if #available(iOS 10, macOS 10.12, *) {
    // 在 iOS 使用 iOS 10 的 API, 在 macOS 使用 macOS 10.12 的 API
} else {
    // 使用先前版本的 iOS 和 macOS 的 API
}

以上可用性條件指定乖阵,if 語句的代碼塊僅僅在 iOS 10 或 macOS 10.12 及更高版本才運(yùn)行宣赔。最后一個參數(shù),*瞪浸,是必須的儒将,用于指定在所有其它平臺中,如果版本號高于你的設(shè)備指定的最低版本对蒲,if 語句的代碼塊將會運(yùn)行钩蚊。

在它一般的形式中,可用性條件使用了一個平臺名字和版本的列表蹈矮。平臺名字可以是 iOS砰逻,macOSwatchOStvOS——請?jiān)L問 特性 來獲取完整列表泛鸟。除了指定像 iOS 8 或 macOS 10.10 的大版本號蝠咆,也可以指定像 iOS 11.2.6 以及 macOS 10.13.3 的小版本號。

if #available(平臺名稱 版本號, ..., *) {
    APIs 可用谈况,語句將執(zhí)行
} else {
    APIs 不可用勺美,語句將不執(zhí)行
}

繼續(xù)閱讀 Swift - 函數(shù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末递胧,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子赡茸,更是在濱河造成了極大的恐慌缎脾,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件占卧,死亡現(xiàn)場離奇詭異遗菠,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)华蜒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門辙纬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人叭喜,你說我怎么就攤上這事贺拣。” “怎么了捂蕴?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵譬涡,是天一觀的道長。 經(jīng)常有香客問我啥辨,道長涡匀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任溉知,我火速辦了婚禮陨瘩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘级乍。我一直安慰自己舌劳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布卡者。 她就那樣靜靜地躺著蒿囤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪崇决。 梳的紋絲不亂的頭發(fā)上材诽,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機(jī)與錄音恒傻,去河邊找鬼脸侥。 笑死,一個胖子當(dāng)著我的面吹牛盈厘,可吹牛的內(nèi)容都是我干的睁枕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼外遇!你這毒婦竟也來了注簿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤跳仿,失蹤者是張志新(化名)和其女友劉穎诡渴,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體菲语,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡妄辩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了山上。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片眼耀。...
    茶點(diǎn)故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖佩憾,靈堂內(nèi)的尸體忽然破棺而出哮伟,到底是詐尸還是另有隱情,我是刑警寧澤鸯屿,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布澈吨,位于F島的核電站把敢,受9級特大地震影響寄摆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜修赞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一婶恼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧柏副,春花似錦勾邦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至荔泳,卻和暖如春蕉饼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背玛歌。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工昧港, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人支子。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓创肥,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子叹侄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容