變量和常量
在Swift中娄琉,有兩種方法可以存儲數據:變量和常量往果。變量是可以隨時更改其值的數據存儲役听,而常量是可以設置一次且永不更改的數據存儲喂柒。因此,變量的值可以變化禾嫉,常量的值是恒定的灾杰。
在Swift中,您可以使用var
關鍵字創(chuàng)建變量熙参,您可以隨時更改它艳吠,如下所示:
var name = "Tim McGraw"
name = "Romeo"
使用let
關鍵字創(chuàng)建常量,不可更改孽椰,如下所示:
let name = "Tim McGraw"
name = "Romeo"
//對常量進行更改時Xcode會報錯:"you're trying to change a constant and you can't do that."
Swift開發(fā)人員非常傾向于盡可能使用常量昭娩,因為它使您的代碼更易于理解。事實上黍匾,在最新版本的Swift中栏渺,Xcode實際上會告訴你,如果你做了一個變量锐涯,那么永遠不要改變它磕诊!
重要說明:
1.變量和常量名稱在代碼中必須唯一。如果您嘗試兩次使用相同的變量名,則會出現錯誤霎终。
2.如果知道值永遠不會改變滞磺,則可以使用let
優(yōu)化以使代碼運行更快。
數據類型
字符串(String
)
整數(Int
)
浮點數(Float
)
雙精度數(Double
)
布爾型(Bool
)
//字符串
var name: String
name = "Tim McGraw"
//整數
var age: Int
age = 25
//浮點數(32位浮點數)
var longitude: Float
longitude = -86.783333
//雙精度數(64位浮點數)
var latitude: Double
latitude = 36.166667
//布爾型
var stayOutTooLate: Bool
stayOutTooLate = true
類型安全和類型推斷
Swift 是一個類型安全(type safe)的語言莱褒。類型安全的語言可以讓你清楚地知道代碼要處理的值的類型击困。如果你的代碼需要一個String,你絕對不可能不小心傳進去一個Int广凸。
由于 Swift 是類型安全的阅茶,所以它會在編譯你的代碼時進行類型檢查(type checks),并把不匹配的類型標記為錯誤谅海。這可以讓你在開發(fā)的時候盡早發(fā)現并修復錯誤脸哀。
當你要處理不同類型的值時,類型檢查可以幫你避免錯誤胁赢。然而企蹭,這并不是說你每次聲明常量和變量的時候都需要顯式指定類型。如果你沒有顯式指定類型智末,Swift 會使用類型推斷(type inference)來選擇合適的類型谅摄。有了類型推斷,編譯器可以在編譯代碼的時候自動推斷出表達式的類型系馆。原理很簡單送漠,只要檢查你賦的值即可。
因為有類型推斷由蘑,和 C 或者 Objective-C 比起來 Swift 很少需要聲明類型闽寡。常量和變量雖然需要明確類型,但是大部分工作并不需要你自己來完成尼酿。
當你聲明常量或者變量并賦初值的時候類型推斷非常有用爷狈。當你在聲明常量或者變量的時候賦給它們一個字面量(literal value 或 literal)即可觸發(fā)類型推斷。(字面量就是會直接出現在你代碼中的值裳擎,比如 42 和 3.14159 涎永。)
例如,如果你給一個新常量賦值 42 并且沒有標明類型鹿响,Swift 可以推斷出常量類型是 Int
羡微,因為你給它賦的初始值看起來像一個整數:
let meaningOfLife = 42
// meaningOfLife 會被推測為 Int 類型
同理,如果你沒有給浮點字面量標明類型惶我,Swift 會推斷你想要的是 Double
:
let pi = 3.14159
// pi 會被推測為 Double 類型
當推斷浮點數的類型時妈倔,Swift 總是會選擇 Double
而不是Float
。
如果表達式中同時出現了整數和浮點數绸贡,會被推斷為 Double
類型:
let anotherPi = 3 + 0.14159
// anotherPi 會被推測為 Double 類型
原始值 3 沒有顯式聲明類型盯蝴,而表達式中出現了一個浮點字面量毅哗,所以表達式會被推斷為 Double
類型。
運算符
運算符分為一元结洼、二元和三元運算符:
- 一元運算符對單一操作對象操作(如
-a
)黎做。一元運算符分前置運算符和后置運算符叉跛,前置運算符需緊跟在操作對象之前(如!b
)松忍,后置運算符需緊跟在操作對象之后(如c!
)。 - 二元運算符操作兩個操作對象(如
2 + 3
)筷厘,是中置的鸣峭,因為它們出現在兩個操作對象之間。 - 三元運算符操作三個操作對象酥艳,和 C 語言一樣摊溶,Swift 只有一個三元運算符,就是三目運算符(
a ? b : c
)充石。
賦值運算符
賦值運算符(a = b
)莫换,表示用 b
的值來初始化或更新 a
的值:
let b = 10
var a = 5
a = b
// a 現在等于 10
如果賦值的右邊是一個多元組,它的元素可以馬上被分解成多個常量或變量:
let (x, y) = (1, 2)
// 現在 x 等于 1骤铃,y 等于 2
與 C 語言和 Objective-C 不同拉岁,Swift 的賦值操作并不返回任何值。所以以下代碼是錯誤的:
if x = y {
// 此句錯誤, 因為 x = y 并不返回任何值
}
這個特性使你無法把(==
)錯寫成(=
)惰爬,由于 if x = y
是錯誤代碼喊暖,Swift 能幫你避免此類錯誤發(fā)生馒稍。
算術運算符
Swift 中所有數值類型都支持了基本的四則算術運算符:
- 加法(
+
) - 減法(
-
) - 乘法(
*
) - 除法(
/
)
1 + 2 // 等于 3
5 - 3 // 等于 2
2 * 3 // 等于 6
10.0 / 2.5 // 等于 4.0
與 C 語言和 Objective-C 不同的是兽愤,Swift 默認情況下不允許在數值運算中出現溢出情況。但是你可以使用 Swift 的溢出運算符來實現溢出運算(如 a &+ b
)肥缔。
加法運算符也可用于 String 的拼接:
"hello, " + "world" // 等于 "hello, world"
求余運算符(a % b
)是計算 b
的多少倍剛剛好可以容入a
丛版,返回多出來的那部分(余數)巩掺。
在 Swift 中可以表達為:
9 % 4 // 等于 1
為了得到 a % b
的結果,%
計算了以下等式页畦,并輸出余數
作為結果:
a = (b × 倍數) + 余數
當倍數
取最大值的時候胖替,就會剛好可以容入 a
中。
注意:
求余運算符(%
)在其他語言也叫取模運算符寇漫。但是嚴格說來刊殉,我們看該運算符對負數的操作結果,「求余」比「取闹莞欤」更合適些记焊。
在對負數 b
求余時,b
的符號會被忽略栓撞。這意味著 a % b
和 a % -b
的結果是相同的遍膜。
一元負號符(-
)寫在操作數之前碗硬,中間沒有空格。數值的正負號可以使用前綴 -
(即一元負號符)來切換:
let three = 3
let minusThree = -three // minusThree 等于 -3
let plusThree = -minusThree // plusThree 等于 3, 或 "負負3"
一元正號符(+
)不做任何改變地返回操作數的值:
let minusSix = -6
let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6
雖然一元正號符什么都不會改變瓢颅,但當你在使用一元負號來表達負數時恩尾,你可以使用一元正號來表達正數,如此你的代碼會具有對稱美挽懦。
組合賦值運算符
如同 C 語言翰意,Swift 也提供把其他運算符和賦值運算(=
)組合的組合賦值運算符,組合加運算(+=
)是其中一個例子:
var a = 1
a += 2
// a 現在是 3
表達式 a += 2
是 a = a + 2
的簡寫信柿,一個組合加運算就是把加法運算和賦值運算組合成進一個運算符里冀偶,同時完成兩個運算任務。
注意:
復合賦值運算沒有返回值渔嚷,let b = a += 2
這類代碼是錯誤进鸠。這不同于上面提到的自增和自減運算符。
比較運算符(Comparison Operators)
所有標準 C 語言中的比較運算符都可以在 Swift 中使用:
- 等于(
a == b
) - 不等于(
a != b
) - 大于(
a > b
) - 小于(
a < b
) - 大于等于(
a >= b
) - 小于等于(
a <= b
)
注意: Swift 也提供恒等(
===
)和不恒等(!==
)這兩個比較符來判斷兩個對象是否引用同一個對象實例形病。
每個比較運算都返回了一個標識表達式是否成立的布爾值:
1 == 1 // true, 因為 1 等于 1
2 != 1 // true, 因為 2 不等于 1
2 > 1 // true, 因為 2 大于 1
1 < 2 // true, 因為 1 小于2
1 >= 1 // true, 因為 1 大于等于 1
2 <= 1 // false, 因為 2 并不小于等于 1
比較運算多用于條件語句客年,如if
條件:
let name = "world"
if name == "world" {
print("hello, world")
} else {
print("I'm sorry \(name), but I don't recognize you")
}
// 輸出 "hello, world", 因為 `name` 就是等于 "world"
如果兩個元組的元素相同,且長度相同的話漠吻,元組就可以被比較量瓜。比較元組大小會按照從左到右、逐值比較的方式侥猩,直到發(fā)現有兩個值不等時停止榔至。如果所有的值都相等,那么這一對元組我們就稱它們是相等的欺劳。例如:
(1, "zebra") < (2, "apple") // true唧取,因為 1 小于 2
(3, "apple") < (3, "bird") // true,因為 3 等于 3划提,但是 apple 小于 bird
(4, "dog") == (4, "dog") // true枫弟,因為 4 等于 4,dog 等于 dog
在上面的例子中鹏往,你可以看到淡诗,在第一行中從左到右的比較行為。因為1
小于2
伊履,所以(1, "zebra")
小于(2, "apple")
韩容,不管元組剩下的值如何。所以"zebra"
大于"apple"
對結果沒有任何影響唐瀑,因為元組的比較結果已經被第一個元素決定了群凶。不過,當元組的第一個元素相同時候哄辣,第二個元素將會用作比較-第二行和第三行代碼就發(fā)生了這樣的比較请梢。
當元組中的元素都可以被比較時赠尾,你也可以使用這些運算符來比較它們的大小。例如毅弧,像下面展示的代碼气嫁,你可以比較兩個類型為 (String, Int)
的元組,因為 Int
和 String
類型的值可以比較够坐。相反寸宵,Bool
不能被比較,也意味著存有布爾類型的元組不能被比較咆霜。
("blue", -1) < ("purple", 1) // 正常邓馒,比較的結果為 true
("blue", false) < ("purple", true) // 錯誤嘶朱,因為 < 不能比較布爾類型
注意:
Swift 標準庫只能比較七個以內元素的元組比較函數蛾坯。如果你的元組元素超過七個時,你需要自己實現比較運算符疏遏。
三目運算符(Ternary Conditional Operator)
三目運算符的特殊在于它是有三個操作數的運算符脉课,它的形式是 問題 ? 答案 1 : 答案 2
。它簡潔地表達根據 問題
成立與否作出二選一的操作财异。如果 問題
成立倘零,返回 答案 1
的結果;反之返回 答案 2
的結果戳寸。
三目運算符是以下代碼的縮寫形式:
if question {
answer1
} else {
answer2
}
這里有個計算表格行高的例子呈驶。如果有表頭,那行高應比內容高度要高出 50 點疫鹊;如果沒有表頭袖瞻,只需高出 20 點:
let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
// rowHeight 現在是 90
上面的寫法比下面的代碼更簡潔:
let contentHeight = 40
let hasHeader = true
var rowHeight = contentHeight
if hasHeader {
rowHeight = rowHeight + 50
} else {
rowHeight = rowHeight + 20
}
// rowHeight 現在是 90
第一段代碼例子使用了三目運算,所以一行代碼就能讓我們得到正確答案拆吆。這比第二段代碼簡潔得多聋迎,無需將 rowHeight
定義成變量,因為它的值無需在 if
語句中改變枣耀。
三目運算提供有效率且便捷的方式來表達二選一的選擇霉晕。需要注意的事,過度使用三目運算符會使簡潔的代碼變的難懂捞奕。我們應避免在一個組合語句中使用多個三目運算符牺堰。
空合運算符(Nil Coalescing Operator)
空合運算符(a ?? b
)將對可選類型 a
進行空判斷,如果 a
包含一個值就進行解封颅围,否則就返回一個默認值 b
伟葫。表達式 a
必須是 Optional 類型。默認值 b
的類型必須要和 a
存儲值的類型保持一致谷浅。
空合運算符是對以下代碼的簡短表達方法:
a != nil ? a! : b
上述代碼使用了三目運算符扒俯。當可選類型 a
的值不為空時奶卓,進行強制解封(a!
),訪問 a
中的值撼玄;反之返回默認值 b
夺姑。無疑空合運算符(??
)提供了一種更為優(yōu)雅的方式去封裝條件判斷和解封兩種行為,顯得簡潔以及更具可讀性掌猛。
注意: 如果
a
為非空值(non-nil
)盏浙,那么值b
將不會被計算。這也就是所謂的短路求值荔茬。
下文例子采用空合運算符废膘,實現了在默認顏色名和可選自定義顏色名之間抉擇:
let defaultColorName = "red"
var userDefinedColorName: String? //默認值為 nil
var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 的值為空,所以 colorNameToUse 的值為 "red"
userDefinedColorName
變量被定義為一個可選的 String
類型慕蔚,默認值為 nil
丐黄。由于 userDefinedColorName
是一個可選類型,我們可以使用空合運算符去判斷其值孔飒。在上一個例子中灌闺,通過空合運算符為一個名為 colorNameToUse
的變量賦予一個字符串類型初始值。 由于 userDefinedColorName
值為空坏瞄,因此表達式 userDefinedColorName ?? defaultColorName
返回 defaultColorName
的值桂对,即 red
。
另一種情況鸠匀,分配一個非空值(non-nil
)給 userDefinedColorName
蕉斜,再次執(zhí)行空合運算,運算結果為封包在 userDefaultColorName
中的值缀棍,而非默認值宅此。
userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 非空,因此 colorNameToUse 的值為 "green"
區(qū)間運算符(Range Operators)
Swift 提供了幾種方便表達一個區(qū)間的值的區(qū)間運算符睦柴。
閉區(qū)間運算符(a...b
)定義一個包含從 a
到 b
(包括 a
和 b
)的所有值的區(qū)間诽凌。a
的值不能超過 b
。 ? 閉區(qū)間運算符在迭代一個區(qū)間的所有值時是非常有用的坦敌,如在 for-in
循環(huán)中:
for index in 1...5 {
print("\(index) * 5 = \(index * 5)")
}
// 1 * 5 = 5
// 2 * 5 = 10
// 3 * 5 = 15
// 4 * 5 = 20
// 5 * 5 = 25
半開區(qū)間運算符(a..<b
)定義一個從 a
到 b
但不包括 b
的區(qū)間侣诵。 之所以稱為半開區(qū)間,是因為該區(qū)間包含第一個值而不包括最后的值狱窘。
半開區(qū)間的實用性在于當你使用一個從 0 開始的列表(如數組)時杜顺,非常方便地從0數到列表的長度。
let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count {
print("第 \(i + 1) 個人叫 \(names[i])")
}
// 第 1 個人叫 Anna
// 第 2 個人叫 Alex
// 第 3 個人叫 Brian
// 第 4 個人叫 Jack
數組有 4 個元素蘸炸,但 0..<count
只數到3(最后一個元素的下標)躬络,因為它是半開區(qū)間。
閉區(qū)間操作符有另一個表達形式搭儒,可以表達往一側無限延伸的區(qū)間 —— 例如穷当,一個包含了數組從索引 2 到結尾的所有值的區(qū)間提茁。在這些情況下,你可以省略掉區(qū)間操作符一側的值馁菜。這種區(qū)間叫做單側區(qū)間茴扁,因為操作符只有一側有值。例如:
for name in names[2...] {
print(name)
}
// Brian
// Jack
for name in names[...2] {
print(name)
}
// Anna
// Alex
// Brian
半開區(qū)間操作符也有單側表達形式汪疮,附帶上它的最終值峭火。就像你使用區(qū)間去包含一個值,最終值并不會落在區(qū)間內智嚷。例如:
for name in names[..<2] {
print(name)
}
// Anna
// Alex
單側區(qū)間不止可以在下標里使用卖丸,也可以在別的情境下使用。你不能遍歷省略了初始值的單側區(qū)間盏道,因為遍歷的開端并不明顯稍浆。你可以遍歷一個省略最終值的單側區(qū)間;然而摇天,由于這種區(qū)間無限延伸的特性粹湃,請保證你在循環(huán)里有一個結束循環(huán)的分支。你也可以查看一個單側區(qū)間是否包含某個特定的值泉坐,就像下面展示的那樣。
let range = ...5
range.contains(7) // false
range.contains(4) // true
range.contains(-1) // true
邏輯運算符(Logical Operators)
邏輯運算符的操作對象是邏輯布爾值裳仆。Swift 支持基于 C 語言的三個標準邏輯運算腕让。
- 邏輯非(
!a
) - 邏輯與(
a && b
) - 邏輯或(
a || b
)
邏輯非運算符(!a
)對一個布爾值取反,使得 true
變 false
歧斟,false
變 true
纯丸。
它是一個前置運算符,需緊跟在操作數之前静袖,且不加空格觉鼻。讀作 非 a
,例子如下:
let allowedEntry = false
if !allowedEntry {
print("ACCESS DENIED")
}
// 輸出 "ACCESS DENIED"
if !allowedEntry
語句可以讀作「如果非 allowedEntry」队橙,接下一行代碼只有在「非 allowedEntry」為 true
坠陈,即 allowEntry
為 false
時被執(zhí)行。
在示例代碼中捐康,小心地選擇布爾常量或變量有助于代碼的可讀性仇矾,并且避免使用雙重邏輯非運算,或混亂的邏輯語句解总。
邏輯與運算符(a && b
)表達了只有 a
和 b
的值都為 true
時贮匕,整個表達式的值才會是 true
。
只要任意一個值為 false
花枫,整個表達式的值就為 false
刻盐。事實上掏膏,如果第一個值為 false
,那么是不去計算第二個值的敦锌,因為它已經不可能影響整個表達式的結果了壤追。這被稱做短路計算(short-circuit evaluation)。
以下例子供屉,只有兩個 Bool
值都為 true
的時候才允許進入 if:
let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 輸出 "ACCESS DENIED"
邏輯或運算符(a || b
)是一個由兩個連續(xù)的 |
組成的中置運算符行冰。它表示了兩個邏輯表達式的其中一個為 true
,整個表達式就為 true
伶丐。
同邏輯與運算符類似悼做,邏輯或也是「短路計算」的,當左端的表達式為 true
時哗魂,將不計算右邊的表達式了肛走,因為它不可能改變整個表達式的值了。
以下示例代碼中录别,第一個布爾值(hasDoorKey
)為 false
朽色,但第二個值(knowsOverridePassword
)為 true
,所以整個表達是 true
组题,于是允許進入:
let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 輸出 "Welcome!"
我們可以組合多個邏輯運算符來表達一個復合邏輯:
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 輸出 "Welcome!"
這個例子使用了含多個 &&
和 ||
的復合邏輯葫男。但無論怎樣,&&
和 ||
始終只能操作兩個值崔列。所以這實際是三個簡單邏輯連續(xù)操作的結果梢褐。我們來解讀一下:
如果我們輸入了正確的密碼并通過了視網膜掃描,或者我們有一把有效的鑰匙赵讯,又或者我們知道緊急情況下重置的密碼盈咳,我們就能把門打開進入。
前兩種情況边翼,我們都不滿足鱼响,所以前兩個簡單邏輯的結果是 false
,但是我們是知道緊急情況下重置的密碼的组底,所以整個復雜表達式的值還是 true
丈积。
注意: Swift 邏輯操作符
&&
和||
是左結合的,這意味著擁有多元邏輯操作符的復合表達式優(yōu)先計算最左邊的子表達式斤寇。
為了一個復雜表達式更容易讀懂桶癣,在合適的地方使用括號來明確優(yōu)先級是很有效的,雖然它并非必要的娘锁。在上個關于門的權限的例子中牙寞,我們給第一個部分加個括號,使它看起來邏輯更明確:
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 輸出 "Welcome!"
這括號使得前兩個值被看成整個邏輯表達中獨立的一個部分。雖然有括號和沒括號的輸出結果是一樣的间雀,但對于讀代碼的人來說有括號的代碼更清晰悔详。可讀性比簡潔性更重要惹挟,請在可以讓你代碼變清晰的地方加個括號吧茄螃!
字符串插值
字符串插值是一種構建新字符串的方式,可以在其中包含常量连锯、變量归苍、字面量和表達式。字符串字面量和多行字符串字面量都可以使用字符串插值运怖。 您插入的字符串字面量的每一項都在以反斜線為前綴的圓括號中:
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message 是 "3 times 2.5 is 7.5"
在上面的例子中拼弃,multiplier
作為\(multiplier)
被插入到一個字符串常量量中。 當創(chuàng)建字符串執(zhí)行插值計算時此占位符會被替換為multiplier
實際的值摇展。
multiplier
的值也作為字符串中后面表達式的一部分吻氧。 該表達式計算Double(multiplier) * 2.5
的值并將結果 (7.5
) 插入到字符串中。 在這個例子中咏连,表達式寫為\(Double(multiplier) * 2.5)
并包含在字符串字面量中盯孙。
注意:
插值字符串中寫在括號中的表達式不能包含非轉義反斜杠 (\
),并且不能包含回車或換行符祟滴。不過振惰,插值字符串可以包含其他字面量。
數組(Arrays)
數組使用有序列表存儲同一類型的多個值踱启。相同的值可以多次出現在一個數組的不同位置中报账。
注意: Swift 的
Array
類型被橋接到Foundation
中的NSArray
類。
數組的簡單語法
寫 Swift 數組應該遵循像Array<Element>
這樣的形式埠偿,其中Element
是這個數組中唯一允許存在的數據類型。我們也可以使用像[Element]
這樣的簡單語法榜晦。盡管兩種形式在功能上是一樣的冠蒋,但是推薦較短的那種,而且在本文中都會使用這種形式來使用數組乾胶。
創(chuàng)建一個空數組
我們可以使用構造語法來創(chuàng)建一個由特定數據類型構成的空數組:
var someInts = [Int]()
print("someInts is of type [Int] with \(someInts.count) items.")
// 打印 "someInts is of type [Int] with 0 items."
注意抖剿,通過構造函數的類型,someInts
的值類型被推斷為[Int]
识窿。
或者斩郎,如果代碼上下文中已經提供了類型信息,例如一個函數參數或者一個已經定義好類型的常量或者變量喻频,我們可以使用空數組語句創(chuàng)建一個空數組缩宜,它的寫法很簡單:[]
(一對空方括號):
someInts.append(3)
// someInts 現在包含一個 Int 值
someInts = []
// someInts 現在是空數組,但是仍然是 [Int] 類型的。
創(chuàng)建一個帶有默認值的數組
Swift 中的Array
類型還提供一個可以創(chuàng)建特定大小并且所有數據都被默認的構造方法锻煌。我們可以把準備加入新數組的數據項數量(count
)和適當類型的初始值(repeating
)傳入數組構造函數:
var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles 是一種 [Double] 數組妓布,等價于 [0.0, 0.0, 0.0]
通過兩個數組相加創(chuàng)建一個數組
我們可以使用加法操作符(+
)來組合兩種已存在的相同類型數組。新數組的數據類型會被從兩個數組的數據類型中推斷出來:
var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles 被推斷為 [Double]宋梧,等價于 [2.5, 2.5, 2.5]
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles 被推斷為 [Double]匣沼,等價于 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
用數組字面量構造數組
我們可以使用數組字面量來進行數組構造,這是一種用一個或者多個數值構造數組的簡單方法捂龄。數組字面量是一系列由逗號分割并由方括號包含的數值:
[value 1, value 2, value 3]
释涛。
下面這個例子創(chuàng)建了一個叫做shoppingList
并且存儲String
的數組:
var shoppingList: [String] = ["Eggs", "Milk"]
// shoppingList 已經被構造并且擁有兩個初始項。
shoppingList
變量被聲明為“字符串值類型的數組“倦沧,記作[String]
唇撬。 因為這個數組被規(guī)定只有String
一種數據結構,所以只有String
類型可以在其中被存取刀脏。 在這里局荚,shoppingList
數組由兩個String
值("Eggs"
和"Milk"
)構造诱建,并且由數組字面量定義栈戳。
注意:
shoppingList
數組被聲明為變量(var
關鍵字創(chuàng)建)而不是常量(let
創(chuàng)建)是因為以后可能會有更多的數據項被插入其中魏保。
在這個例子中黍檩,字面量僅僅包含兩個String
值红柱。匹配了該數組的變量聲明(只能包含String
的數組)渣磷,所以這個字面量的分配過程可以作為用兩個初始項來構造shoppingList
的一種方式厉膀。
由于 Swift 的類型推斷機制兰吟,當我們用字面量構造只擁有相同類型值數組的時候杭跪,我們不必把數組的類型定義清楚仙逻。 shoppingList
的構造也可以這樣寫:
var shoppingList = ["Eggs", "Milk"]
因為所有數組字面量中的值都是相同的類型,Swift 可以推斷出[String]
是shoppingList
中變量的正確類型涧尿。
訪問和修改數組
我們可以通過數組的方法和屬性來訪問和修改數組系奉,或者使用下標語法。
可以使用數組的只讀屬性count
來獲取數組中的數據項數量:
print("The shopping list contains \(shoppingList.count) items.")
// 輸出 "The shopping list contains 2 items."(這個數組有2個項)
使用布爾屬性isEmpty
作為一個縮寫形式去檢查count
屬性是否為0
:
if shoppingList.isEmpty {
print("The shopping list is empty.")
} else {
print("The shopping list is not empty.")
}
// 打印 "The shopping list is not empty."(shoppinglist 不是空的)
也可以使用append(_:)
方法在數組后面添加新的數據項:
shoppingList.append("Flour")
// shoppingList 現在有3個數據項姑廉,有人在攤煎餅
除此之外缺亮,使用加法賦值運算符(+=
)也可以直接在數組后面添加一個或多個擁有相同類型的數據項:
shoppingList += ["Baking Powder"]
// shoppingList 現在有四項了
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
// shoppingList 現在有七項了
可以直接使用下標語法來獲取數組中的數據項,把我們需要的數據項的索引值放在直接放在數組名稱的方括號中:
var firstItem = shoppingList[0]
// 第一項是 "Eggs"
注意:
第一項在數組中的索引值是0
而不是1
桥言。 Swift 中的數組索引總是從零開始萌踱。
我們也可以用下標來改變某個已有索引值對應的數據值:
shoppingList[0] = "Six eggs"
// 其中的第一項現在是 "Six eggs" 而不是 "Eggs"
還可以利用下標來一次改變一系列數據值,即使新數據和原有數據的數量是不一樣的号阿。下面的例子把"Chocolate Spread"
并鸵,"Cheese"
,和"Butter"
替換為"Bananas"
和 "Apples"
:
shoppingList[4...6] = ["Bananas", "Apples"]
// shoppingList 現在有6項
注意:
不可以用下標訪問的形式去在數組尾部添加新項扔涧。
調用數組的insert(_:at:)
方法來在某個具體索引值之前添加數據項:
shoppingList.insert("Maple Syrup", at: 0)
// shoppingList 現在有7項
// "Maple Syrup" 現在是這個列表中的第一項
這次insert(_:at:)
方法調用把值為"Maple Syrup"
的新數據項插入列表的最開始位置园担,并且使用0
作為索引值。
類似的我們可以使用remove(at:)
方法來移除數組中的某一項。這個方法把數組在特定索引值中存儲的數據項移除并且返回這個被移除的數據項(我們不需要的時候就可以無視它):
let mapleSyrup = shoppingList.remove(at: 0)
// 索引值為0的數據項被移除
// shoppingList 現在只有6項粉铐,而且不包括 Maple Syrup
// mapleSyrup 常量的值等于被移除數據項的值 "Maple Syrup"
注意:
如果我們試著對索引越界的數據進行檢索或者設置新值的操作疼约,會引發(fā)一個運行期錯誤。我們可以使用索引值和數組的count
屬性進行比較來在使用某個索引之前先檢驗是否有效蝙泼。除了當count
等于 0 時(說明這是個空數組)程剥,最大索引值一直是count - 1
,因為數組都是零起索引汤踏。
數據項被移除后數組中的空出項會被自動填補织鲸,所以現在索引值為0
的數據項的值再次等于"Six eggs"
:
firstItem = shoppingList[0]
// firstItem 現在等于 "Six eggs"
如果我們只想把數組中的最后一項移除,可以使用removeLast()
方法而不是remove(at:)
方法來避免我們需要獲取數組的count
屬性溪胶。就像后者一樣搂擦,前者也會返回被移除的數據項:
let apples = shoppingList.removeLast()
// 數組的最后一項被移除了
// shoppingList 現在只有5項,不包括 Apples
// apples 常量的值現在等于 "Apples" 字符串
數組的遍歷
我們可以使用for-in
循環(huán)來遍歷所有數組中的數據項:
for item in shoppingList {
print(item)
}
// Six eggs
// Milk
// Flour
// Baking Powder
// Bananas
如果我們同時需要每個數據項的值和索引值哗脖,可以使用enumerated()
方法來進行數組遍歷瀑踢。enumerated()
返回一個由每一個數據項索引值和數據值組成的元組。我們可以把這個元組分解成臨時常量或者變量來進行遍歷:
for (index, value) in shoppingList. enumerated() {
print("Item \(String(index + 1)): \(value)")
}
// Item 1: Six eggs
// Item 2: Milk
// Item 3: Flour
// Item 4: Baking Powder
// Item 5: Bananas
字典
字典是一種存儲多個相同類型的值的容器才避。每個值(value)都關聯唯一的鍵(key)橱夭,鍵作為字典中的這個值數據的標識符。和數組中的數據項不同桑逝,字典中的數據項并沒有具體順序棘劣。我們在需要通過標識符(鍵)訪問數據的時候使用字典,這種方法很大程度上和我們在現實世界中使用字典查字義的方法一樣楞遏。
注意:
Swift 的Dictionary
類型被橋接到Foundation
的NSDictionary
類茬暇。
字典類型簡化語法
Swift 的字典使用Dictionary<Key, Value>
定義,其中Key
是字典中鍵的數據類型寡喝,Value
是字典中對應于這些鍵所存儲值的數據類型糙俗。
注意:
一個字典的Key
類型必須遵循Hashable
協議,就像Set
的值類型预鬓。
我們也可以用[Key: Value]
這樣簡化的形式去創(chuàng)建一個字典類型臼节。雖然這兩種形式功能上相同,但是后者是首選珊皿,并且這本指導書涉及到字典類型時通篇采用后者。
創(chuàng)建一個空字典
我們可以像數組一樣使用構造語法創(chuàng)建一個擁有確定類型的空字典:
var namesOfIntegers = [Int: String]()
// namesOfIntegers 是一個空的 [Int: String] 字典
這個例子創(chuàng)建了一個[Int: String]
類型的空字典來儲存整數的英語命名巨税。它的鍵是Int
型蟋定,值是String
型。
如果上下文已經提供了類型信息草添,我們可以使用空字典字面量來創(chuàng)建一個空字典驶兜,記作[:]
(中括號中放一個冒號):
namesOfIntegers[16] = "sixteen"
// namesOfIntegers 現在包含一個鍵值對
namesOfIntegers = [:]
// namesOfIntegers 又成為了一個 [Int: String] 類型的空字典
用字典字面量創(chuàng)建字典
我們可以使用字典字面量來構造字典,這和我們剛才介紹過的數組字面量擁有相似語法。字典字面量是一種將一個或多個鍵值對寫作Dictionary
集合的快捷途徑抄淑。
一個鍵值對是一個key
和一個value
的結合體屠凶。在字典字面量中,每一個鍵值對的鍵和值都由冒號分割肆资。這些鍵值對構成一個列表矗愧,其中這些鍵值對由方括號包含、由逗號分割:
[key 1: value 1, key 2: value 2, key 3: value 3]
下面的例子創(chuàng)建了一個存儲國際機場名稱的字典郑原。在這個字典中鍵是三個字母的國際航空運輸相關代碼唉韭,值是機場名稱:
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
airports
字典被聲明為一種[String: String]
類型,這意味著這個字典的鍵和值都是String
類型犯犁。
注意:
airports
字典被聲明為變量(用var
關鍵字)而不是常量(let
關鍵字)因為后來更多的機場信息會被添加到這個示例字典中属愤。
airports
字典使用字典字面量初始化,包含兩個鍵值對酸役。第一對的鍵是YYZ
住诸,值是Toronto Pearson
。第二對的鍵是DUB
涣澡,值是Dublin
贱呐。
這個字典語句包含了兩個String: String
類型的鍵值對。它們對應airports
變量聲明的類型(一個只有String
鍵和String
值的字典)所以這個字典字面量的任務是構造擁有兩個初始數據項的airport
字典暑塑。
和數組一樣吼句,我們在用字典字面量構造字典時,如果它的鍵和值都有各自一致的類型事格,那么就不必寫出字典的類型惕艳。 airports
字典也可以用這種簡短方式定義:
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
因為這個語句中所有的鍵和值都各自擁有相同的數據類型,Swift 可以推斷出Dictionary<String, String>
是airports
字典的正確類型驹愚。
訪問和修改字典
我們可以通過字典的方法和屬性來訪問和修改字典远搪,或者通過使用下標語法。
和數組一樣逢捺,我們可以通過字典的只讀屬性count
來獲取某個字典的數據項數量:
print("The dictionary of airports contains \(airports.count) items.")
// 打印 "The dictionary of airports contains 2 items."(這個字典有兩個數據項)
使用布爾屬性isEmpty
作為一個縮寫形式去檢查count
屬性是否為0
:
if airports.isEmpty {
print("The airports dictionary is empty.")
} else {
print("The airports dictionary is not empty.")
}
// 打印 "The airports dictionary is not empty."
我們也可以在字典中使用下標語法來添加新的數據項谁鳍。可以使用一個恰當類型的鍵作為下標索引劫瞳,并且分配恰當類型的新值:
airports["LHR"] = "London"
// airports 字典現在有三個數據項
我們也可以使用下標語法來改變特定鍵對應的值:
airports["LHR"] = "London Heathrow"
// "LHR"對應的值 被改為 "London Heathrow
作為另一種下標方法倘潜,字典的updateValue(_:forKey:)
方法可以設置或者更新特定鍵對應的值。就像上面所示的下標示例志于,updateValue(_:forKey:)
方法在這個鍵不存在對應值的時候會設置新值或者在存在時更新已存在的值涮因。和上面的下標方法不同的,updateValue(_:forKey:)
這個方法返回更新值之前的原值伺绽。這樣使得我們可以檢查更新是否成功养泡。
updateValue(_:forKey:)
方法會返回對應值的類型的可選值嗜湃。舉例來說:對于存儲String
值的字典,這個函數會返回一個String?
或者“可選 String
”類型的值澜掩。
如果有值存在于更新前购披,則這個可選值包含了舊值,否則它將會是nil
肩榕。
if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
print("The old value for DUB was \(oldValue).")
}
// 輸出 "The old value for DUB was Dublin."
我們也可以使用下標語法來在字典中檢索特定鍵對應的值刚陡。因為有可能請求的鍵沒有對應的值存在,字典的下標訪問會返回對應值的類型的可選值点把。如果這個字典包含請求鍵所對應的值橘荠,下標會返回一個包含這個存在值的可選值,否則將返回nil
:
if let airportName = airports["DUB"] {
print("The name of the airport is \(airportName).")
} else {
print("That airport is not in the airports dictionary.")
}
// 打印 "The name of the airport is Dublin Airport."
我們還可以使用下標語法來通過給某個鍵的對應值賦值為nil
來從字典里移除一個鍵值對:
airports["APL"] = "Apple Internation"
// "Apple Internation" 不是真的 APL 機場, 刪除它
airports["APL"] = nil
// APL 現在被移除了
此外郎逃,removeValue(forKey:)
方法也可以用來在字典中移除鍵值對哥童。這個方法在鍵值對存在的情況下會移除該鍵值對并且返回被移除的值或者在沒有值的情況下返回nil
:
if let removedValue = airports. removeValue(forKey: "DUB") {
print("The removed airport's name is \(removedValue).")
} else {
print("The airports dictionary does not contain a value for DUB.")
}
// prints "The removed airport's name is Dublin Airport."
字典遍歷
我們可以使用for-in
循環(huán)來遍歷某個字典中的鍵值對。每一個字典中的數據項都以(key, value)
元組形式返回褒翰,并且我們可以使用臨時常量或者變量來分解這些元組:
for (airportCode, airportName) in airports {
print("\(airportCode): \(airportName)")
}
// YYZ: Toronto Pearson
// LHR: London Heathrow
通過訪問keys
或者values
屬性贮懈,我們也可以遍歷字典的鍵或者值:
for airportCode in airports.keys {
print("Airport code: \(airportCode)")
}
// Airport code: YYZ
// Airport code: LHR
for airportName in airports.values {
print("Airport name: \(airportName)")
}
// Airport name: Toronto Pearson
// Airport name: London Heathrow
如果我們只是需要使用某個字典的鍵集合或者值集合來作為某個接受Array
實例的 API 的參數,可以直接使用keys
或者values
屬性構造一個新數組:
let airportCodes = [String](airports.keys)
// airportCodes 是 ["YYZ", "LHR"]
let airportNames = [String](airports.values)
// airportNames 是 ["Toronto Pearson", "London Heathrow"]
Swift 的字典類型是無序集合類型优训。為了以特定的順序遍歷字典的鍵或值朵你,可以對字典的keys
或values
屬性使用sorted()
方法。
條件語句
根據特定的條件執(zhí)行特定的代碼通常是十分有用的揣非。當錯誤發(fā)生時抡医,你可能想運行額外的代碼;或者早敬,當值太大或太小時忌傻,向用戶顯示一條消息。要實現這些功能搞监,你就需要使用條件語句水孩。
Swift 提供兩種類型的條件語句:if
語句和switch
語句。通常琐驴,當條件較為簡單且可能的情況很少時俘种,使用if
語句。而switch
語句更適用于條件較復雜绝淡、有更多排列組合的時候宙刘。并且switch
在需要用到模式匹配(pattern-matching)的情況下會更有用。
If
if
語句最簡單的形式就是只包含一個條件牢酵,只有該條件為true
時荐类,才執(zhí)行相關代碼:
var temperatureInFahrenheit = 30
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
}
// 輸出 "It's very cold. Consider wearing a scarf."
上面的例子會判斷溫度是否小于等于 32 華氏度(水的冰點)。如果是茁帽,則打印一條消息玉罐;否則,不打印任何消息潘拨,繼續(xù)執(zhí)行if
塊后面的代碼吊输。
當然,if
語句允許二選一執(zhí)行铁追,叫做else
從句季蚂。也就是當條件為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
語句鏈接在一起葵腹,來實現更多分支:
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
語句被保留了下來践宴,用于打印既不冷也不熱時的消息。
實際上爷怀,當不需要完整判斷情況的時候阻肩,最后的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ā)if
或else if
分支烤惊,也就不會打印任何消息。
Switch
switch
語句會嘗試把某個值與若干個模式(pattern)進行匹配徒坡。根據第一個匹配成功的模式撕氧,switch
語句會執(zhí)行對應的代碼。當有可能的情況較多時喇完,通常用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 構成,每個由case
關鍵字開始锦溪。為了匹配某些更特定的值不脯,Swift 提供了幾種方法來進行更復雜的模式匹配,這些模式將在本節(jié)的稍后部分提到刻诊。
與if
語句類似防楷,每一個 case 都是代碼執(zhí)行的一條分支。switch
語句會決定哪一條分支應該被執(zhí)行则涯,這個流程被稱作根據給定的值切換(switching)复局。
switch
語句必須是完備的冲簿。這就是說,每一個可能的值都必須至少有一個 case 分支與之對應亿昏。在某些不可能涵蓋所有值的情況下峦剔,你可以使用默認(default
)分支來涵蓋其它所有沒有對應的值,這個默認分支必須在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
递礼。 因為switch
語句必須有一個case分支用于覆蓋所有可能的字符惨险,而不僅僅是所有的英文字母,所以switch語句使用default
分支來匹配除了a
和z
外的所有值脊髓,這個分支保證了swith語句的完備性辫愉。
不存在隱式的貫穿
與 C 和 Objective-C 中的switch
語句不同,在 Swift 中供炼,當匹配的 case 分支中的代碼執(zhí)行完畢后一屋,程序會終止switch
語句,而不會繼續(xù)執(zhí)行下一個 case 分支袋哼。這也就是說冀墨,不需要在 case 分支中顯式地使用break
語句。這使得switch
語句更安全涛贯、更易用诽嘉,也避免了因忘記寫break
語句而產生的錯誤。
注意: 雖然在Swift中
break
不是必須的弟翘,但你依然可以在 case 分支中的代碼執(zhí)行完畢前使用break
跳出虫腋。
每一個 case 分支都必須包含至少一條語句。像下面這樣書寫代碼是無效的稀余,因為第一個 case 分支是空的:
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a": // 無效悦冀,這個分支下面沒有語句
case "A":
print("The letter A")
default:
print("Not the letter A")
}
// 這段代碼會報編譯錯誤
不像 C 語言里的switch
語句,在 Swift 中睛琳,switch
語句不會一起匹配"a"
和"A"
盒蟆。相反的,上面的代碼會引起編譯期錯誤:case "a": 不包含任何可執(zhí)行語句
——這就避免了意外地從一個 case 分支貫穿到另外一個师骗,使得代碼更安全历等、也更直觀。
為了讓單個case同時匹配a
和A
辟癌,可以將這個兩個值組合成一個復合匹配寒屯,并且用逗號分開:
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":
print("The letter A")
default:
print("Not the letter A")
}
// 輸出 "The letter A
為了可讀性,符合匹配可以寫成多行形式黍少。
注意: 如果想要顯式貫穿case分支寡夹,請使用
fallthrough
語句处面。
區(qū)間匹配
case 分支的模式也可以是一個值的區(qū)間。下面的例子展示了如何使用區(qū)間匹配來輸出任意數字對應的自然語言格式:
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
都與之進行比較。因為approximateCount
落在了 12 到 100 的區(qū)間患蹂,所以naturalCount
等于"dozens of"
值,并且此后的執(zhí)行跳出了switch
語句砸紊。
元組
我們可以使用元組在同一個switch
語句中測試多個值传于。元組中的元素可以是值,也可以是區(qū)間醉顽。另外沼溜,使用下劃線(_
)來匹配所有可能的值。
下面的例子展示了如何使用一個(Int, Int)
類型的元組來分類下圖中的點(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"
在上面的例子中游添,switch
語句會判斷某個點是否是原點(0, 0)系草,是否在紅色的x軸上,是否在橘黃色的y軸上唆涝,是否在一個以原點為中心的4x4的藍色矩形里找都,或者在這個矩形外面。
不像 C 語言廊酣,Swift 允許多個 case 匹配同一個值能耻。實際上,在這個例子中亡驰,點(0, 0)可以匹配所有四個 case晓猛。但是,如果存在多個匹配凡辱,那么只會執(zhí)行第一個被匹配到的 case 分支戒职。考慮點(0, 0)會首先匹配case (0, 0)
透乾,因此剩下的能夠匹配的分支都會被忽視掉洪燥。
值綁定(Value Bindings)
case 分支允許將匹配的值聲明為臨時常量或變量,并且在case分支體內使用 —— 這種行為被稱為值綁定(value binding)续徽,因為匹配的值在case分支體內蚓曼,與臨時的常量或變量綁定。
下面的例子將下圖中的點(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"
在上面的例子中,switch
語句會判斷某個點是否在紅色的x軸上客情,是否在橘黃色的y軸上其弊,或者不在坐標軸上癞己。
這三個 case 都聲明了常量x
和y
的占位符,用于臨時獲取元組anotherPoint
的一個或兩個值梭伐。第一個 case ——case (let x, 0)
將匹配一個縱坐標為0
的點痹雅,并把這個點的橫坐標賦給臨時的常量x
。類似的糊识,第二個 case ——case (0, let y)
將匹配一個橫坐標為0
的點绩社,并把這個點的縱坐標賦給臨時的常量y
。
一旦聲明了這些臨時的常量赂苗,它們就可以在其對應的 case 分支里使用愉耙。在這個例子中,它們用于打印給定點的類型拌滋。
請注意朴沿,這個switch
語句不包含默認分支。這是因為最后一個 case ——case let(x, y)
聲明了一個可以匹配余下所有值的元組败砂。這使得switch
語句已經完備了赌渣,因此不需要再書寫默認分支。
Where
case 分支的模式可以使用where
語句來判斷額外的條件昌犹。
下面的例子把下圖中的點(x, y)進行了分類:
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"
在上面的例子中坚芜,switch
語句會判斷某個點是否在綠色的對角線x == y
上,是否在紫色的對角線x == -y
上祭隔,或者不在對角線上货岭。
這三個 case 都聲明了常量x
和y
的占位符,用于臨時獲取元組yetAnotherPoint
的兩個值疾渴。這兩個常量被用作where
語句的一部分千贯,從而創(chuàng)建一個動態(tài)的過濾器(filter)。當且僅當where
語句的條件為true
時搞坝,匹配到的 case 分支才會被執(zhí)行搔谴。
就像是值綁定中的例子,由于最后一個 case 分支匹配了余下所有可能的值桩撮,switch
語句就已經完備了敦第,因此不需要再書寫默認分支。
復合匹配
當多個條件可以使用同一種方法來處理時店量,可以將這幾種可能放在同一個case
后面芜果,并且用逗號隔開。當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ā)生了匹配,分支體內的代碼署惯,都能獲取到綁定的值,并且綁定的值都有一樣的類型镣隶。
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
在兩種模式下轻猖,都是整型——這意味著分支體內的代碼,只要case匹配域那,都可以獲取到distance
值咙边。
For-In 循環(huán)
你可以使用 for-in
循環(huán)來遍歷一個集合中的所有元素,例如數組中的元素次员、范圍內的數字或者字符串中的字符败许。
以下例子使用 for-in
遍歷一個數組所有元素:
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
print("Hello, \(name)!")
}
// Hello, Anna!
// Hello, Alex!
// Hello, Brian!
// Hello, Jack!
你也可以通過遍歷一個字典來訪問它的鍵值對。遍歷字典時淑蔚,字典的每項元素會以 (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")
}
// ants have 6 legs
// spiders have 8 legs
// cats have 4 legs
字典的內容理論上是無序的,遍歷元素時的順序是無法確定的带迟。將元素插入字典的順序并不會決定它們被遍歷的順序音羞。
for-in
循環(huán)還可以使用數字范圍。下面的例子用來輸出乘法表的一部分內容:
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
例子中用來進行遍歷的元素是使用閉區(qū)間操作符(...
)表示的從 1
到 5
的數字區(qū)間仓犬。index
被賦值為閉區(qū)間中的第一個數字(1
)嗅绰,然后循環(huán)中的語句被執(zhí)行一次。在本例中,這個循環(huán)只包含一個語句办陷,用來輸出當前 index
值所對應的乘 5 乘法表的結果貌夕。該語句執(zhí)行后,index
的值被更新為閉區(qū)間中的第二個數字(2
)民镜,之后 print(_:separator:terminator:)
函數會再執(zhí)行一次啡专。整個過程會進行到閉區(qū)間結尾為止。
上面的例子中制圈,index
是一個每次循環(huán)遍歷開始時被自動賦值的常量们童。這種情況下,index
在使用前不需要聲明鲸鹦,只需要將它包含在循環(huán)的聲明中慧库,就可以對其進行隱式聲明,而無需使用 let
關鍵字聲明馋嗜。
如果你不需要區(qū)間序列內每一項的值齐板,你可以使用下劃線(_
)替代變量名來忽略這個值:
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"
這個例子計算 base 這個數的 power 次冪(本例中,是 3
的 10
次冪)葛菇,從 1
( 3
的 0
次冪)開始做 3
的乘法甘磨, 進行 10
次,使用 1
到 10
的閉區(qū)間循環(huán)眯停。這個計算并不需要知道每一次循環(huán)中計數器具體的值济舆,只需要執(zhí)行了正確的循環(huán)次數即可。下劃線符號 _
(替代循環(huán)中的變量)能夠忽略當前值莺债,并且不提供循環(huán)遍歷時對值的訪問滋觉。
在某些情況下,你可能不想使用閉區(qū)間齐邦,包括兩個端點椎侠。想象一下,你在一個手表上繪制分鐘的刻度線侄旬》挝担總共 60
個刻度,從 0
分開始儡羔。使用半開區(qū)間運算符(..<
)來表示一個左閉右開的區(qū)間宣羊。
let minutes = 60
for tickMark in 0..<minutes {
// 每一分鐘都渲染一個刻度線(60次)
}
一些用戶可能在其UI中可能需要較少的刻度。他們可以每5分鐘作為一個刻度汰蜘。使用 stride(from:to:by:)
函數跳過不需要的標記仇冯。
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)
}
While 循環(huán)
while
循環(huán)會一直運行一段語句直到條件變成false
。這類循環(huán)適合使用在第一次迭代前族操,迭代次數未知的情況下苛坚。Swift 提供兩種while
循環(huán)形式:
-
while
循環(huán)比被,每次在循環(huán)開始時計算條件是否符合; -
repeat-while
循環(huán)泼舱,每次在循環(huán)結束時計算條件是否符合等缀。
While
while
循環(huán)從計算一個條件開始。如果條件為true
娇昙,會重復運行一段語句尺迂,直到條件變?yōu)?code>false。
下面是 while
循環(huán)的一般格式:
while condition {
statements
}
下面的例子來玩一個叫做蛇和梯子(也叫做滑道和梯子)的小游戲:
游戲的規(guī)則如下:
- 游戲盤面包括 25 個方格冒掌,游戲目標是達到或者超過第 25 個方格噪裕;
- 每一輪,你通過擲一個六面體骰子來確定你移動方塊的步數股毫,移動的路線由上圖中橫向的虛線所示膳音;
- 如果在某輪結束,你移動到了梯子的底部铃诬,可以順著梯子爬上去祭陷;
- 如果在某輪結束,你移動到了蛇的頭部趣席,你會順著蛇的身體滑下去颗胡。
游戲盤面可以使用一個Int
數組來表達。數組的長度由一個finalSquare
常量儲存吩坝,用來初始化數組和檢測最終勝利條件。游戲盤面由 26 個 Int
0 值初始化哑蔫,而不是 25 個(由0
到25
钉寝,一共 26 個):
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
3 號方格是梯子的底部腥沽,會讓你向上移動到 11 號方格逮走,我們使用board[03]
等于+08
(來表示11
和3
之間的差值)。為了對齊語句今阳,這里使用了一元正運算符(+i
)和一元負運算符(-i
)师溅,并且小于 10 的數字都使用 0 補齊(這些語法的技巧不是必要的,只是為了讓代碼看起來更加整潔)盾舌。
玩家由左下角空白處編號為 0 的方格開始游戲墓臭。玩家第一次擲骰子后才會進入游戲盤面:
var square = 0
var diceRoll = 0
while square < finalSquare {
// 擲骰子
diceRoll += 1
if diceRoll == 7 { diceRoll = 1 }
// 根據點數移動
square += diceRoll
if square < board.count {
// 如果玩家還在棋盤上,順著梯子爬上去或者順著蛇滑下去
square += board[square]
}
}
print("Game over!")
本例中使用了最簡單的方法來模擬擲骰子妖谴。 diceRoll
的值并不是一個隨機數窿锉,而是以0
為初始值,之后每一次while
循環(huán),diceRoll
的值增加 1 嗡载,然后檢測是否超出了最大值窑多。當diceRoll
的值等于 7 時,就超過了骰子的最大值洼滚,會被重置為1
埂息。所以diceRoll
的取值順序會一直是 1
,2
判沟,3
耿芹,4
,5
挪哄,6
吧秕,1
,2
等迹炼。
擲完骰子后砸彬,玩家向前移動diceRoll
個方格,如果玩家移動超過了第 25 個方格斯入,這個時候游戲將會結束砂碉,為了應對這種情況,代碼會首先判斷square
的值是否小于board
的count
屬性刻两,只有小于才會在board[square]
上增加square
增蹭,來向前或向后移動(遇到了梯子或者蛇)。
注意:
如果沒有這個檢測(square < board.count
)磅摹,board[square]
可能會越界訪問board
數組滋迈,導致錯誤。
當本輪while
循環(huán)運行完畢户誓,會再檢測循環(huán)條件是否需要再運行一次循環(huán)饼灿。如果玩家移動到或者超過第 25 個方格,循環(huán)條件結果為false
帝美,此時游戲結束碍彭。
while
循環(huán)比較適合本例中的這種情況,因為在 while
循環(huán)開始時悼潭,我們并不知道游戲要跑多久庇忌,只有在達成指定條件時循環(huán)才會結束。
Repeat-While
while
循環(huán)的另外一種形式是repeat-while
舰褪,它和while
的區(qū)別是在判斷循環(huán)條件之前漆枚,先執(zhí)行一次循環(huán)的代碼塊。然后重復循環(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)。finalSquare
残制、board
立砸、square
和diceRoll
的值初始化同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 }
// 根據點數移動
square += diceRoll
} while square < finalSquare
print("Game over!")
檢測完玩家是否踩在梯子或者蛇上之后倔幼,開始擲骰子,然后玩家向前移動diceRoll
個方格爽待,本輪循環(huán)結束损同。
循環(huán)條件(while square < finalSquare
)和while
方式相同,但是只會在循環(huán)結束后進行計算鸟款。在這個游戲中膏燃,repeat-while
表現得比while
循環(huán)更好。repeat-while
方式會在條件判斷square
沒有超出后直接運行square += board[square]
何什,這種方式可以比起前面 while
循環(huán)的版本组哩,可以省去數組越界的檢查。