變量和常量
任何 Swift 中的變量要么不變的犯戏,要么是可變的屑柔。這句話可不要和 Int买乃、 Float 這些變量類型混淆。變量和常量僅僅是一種視角來描繪持有的值是可修改的(即可變性)蝙搔,亦或是不能修改的(即不可變性)缕溉。
要定義一個(gè)常量,使用 let 關(guān)鍵字吃型。舉例來說:
let name = "Jameson"````
如果你想改變 name 的值证鸥,會(huì)發(fā)現(xiàn)沒有辦法做到,然后 Swift 在編譯時(shí)會(huì)拋出一個(gè)錯(cuò)誤勤晚。
```
let name = "Jameson"
name = "Bob"
error: cannot assign to value: 'name' is a 'let' constant
name = "Bob"
~~~~ ^````
要解決它枉层,我們可以使用 var 關(guān)鍵字,來定義一個(gè)可以修改的變量:
var name = "Jameson"
name = "Bob"````
這次代碼就不會(huì)報(bào)錯(cuò)了赐写。
一般來說鸟蜡,你應(yīng)該默認(rèn)去使用 let 關(guān)鍵字,除非知道需要使用到 var 關(guān)鍵字挺邀。這樣的方式將會(huì)從根本上增加代碼的安全性揉忘。如果當(dāng)你定義了常量,之后要去修改它時(shí)端铛,你會(huì)得到錯(cuò)誤提示泣矛,可以到那個(gè)時(shí)候再?zèng)Q定是不是應(yīng)該使用 var 關(guān)鍵字『滩希或者如錯(cuò)誤是給你的提示您朽,應(yīng)該重新去思考當(dāng)前的邏輯流。一般來說换淆,不可變性優(yōu)于可變性虚倒,它可以幫助開發(fā)者少犯錯(cuò)誤,并且更容易編寫代碼产舞。
分號(hào)
在Swift 并不強(qiáng)制要求你在每條語句的結(jié)尾處使用分號(hào)( ; )魂奥,當(dāng)然,你也可以按照 你自己的習(xí)慣添加分號(hào)易猫。有一種情況下必須要用分號(hào)耻煤,即你打算在同一行內(nèi)寫多條獨(dú)立的語句:
let cat = "?"; print(cat)
// 輸出 "?"
注釋
//單行注釋
/*
多行注釋
*/````
####類型推斷
在Swift中,可以不用顯式的制定數(shù)據(jù)類型准颓,swift能夠自行推測出數(shù)據(jù)類型
####斷言
斷言在調(diào)試中非常有用哈蝇,就像別的語言中的斷點(diǎn)調(diào)試。Swift通過一個(gè)全局函數(shù)assert來實(shí)現(xiàn)攘已。它有兩個(gè)參數(shù):一個(gè)表達(dá)式炮赦,一條信息,若表達(dá)式的結(jié)果是false就會(huì)中斷程序并打印那條信息样勃。
```
var age = 34
assert(age<18, "如果你小于18歲你就不是大叔了")
```
####基礎(chǔ)類型
在 Swift 中吠勘,一個(gè)類型被聲明的寫法是通過聲明一個(gè)變量性芬,然后緊跟一個(gè)冒號(hào),然后是類型名稱剧防。例如我們聲明一個(gè)整型植锉,在 Swift 中類型是 Int ,那么你可以如下寫法:
let age: Int = 5````
相似的峭拘,你可以聲明一個(gè)字符串類型:
let name: String = "Jameson"````
Swift 支持類型推斷俊庇,可以不寫具體的類型信息。然后讓編譯器根據(jù)它的初始值來推斷它是什么類型的鸡挠。
let age = 5
let name = "Jameson"````
age 和 name 的類型仍然是 Int 和 String 辉饱,但是這次我們跳過了類型聲明,因?yàn)楹茱@然拣展,5 是 Int 類型鞋囊,而 “Jameson” 是一個(gè)字符串。
記住瞎惫, let 關(guān)鍵字僅僅使值變得不可變溜腐。如果我們預(yù)測這個(gè) age 的值是可變的,而 name 不是可變的瓜喇,那么我們應(yīng)該這么寫:
var age = 5
let name = "Jameson"````
現(xiàn)在如果要更新 age 的值挺益,可以這么做:
var age = 5
let name = "Jameson"
age = 25
print(age)````
布爾值
Swift的布爾值為Bool,它有兩個(gè)布爾常量--true和false乘寒。
浮點(diǎn)數(shù)
Swift有兩種浮點(diǎn)數(shù)類型望众,64位浮點(diǎn)數(shù)的Double和32位的浮點(diǎn)數(shù)Float。
數(shù)值型類型轉(zhuǎn)換
let heigth = 1.73 //
let iHeight = Int(heigth) //把height轉(zhuǎn)成Int類型
let fHeight = Float(heigth) //把height轉(zhuǎn)成Float類型
let dHeight = Double(heigth) //把height轉(zhuǎn)成Double類型
使用字符串
用 print 打印命令或者 String 的字符串總是很方便伞辛。例如烂翰,我想要打印一個(gè)包含變量 age 和變量 name 的語句,可以在兩個(gè) String 變量之間用 + 操作符蚤氏。
let age = "15"
let name = "Robb"
let sentence = name + " is " + age
print(sentence)````
打印結(jié)果為:Robb is 15
改用另外一個(gè)方式來拼接 String甘耿,可以不使用 + 操作符,而是在將每一個(gè)變量放進(jìn)一組括號(hào)中竿滨,并在變量前使用 \ 反斜杠佳恬。
let sentence = "(name) is (age)"
print(sentence)
Robb is 15````
現(xiàn)在也可以看到同樣的效果,但是它更容易閱讀和組合于游。
也許你注意到了毁葱, age 現(xiàn)在是一個(gè) String 類型因?yàn)樗F(xiàn)在是 “15” 而不是 15,沒有了引號(hào)贰剥。這是因?yàn)槿绻粋€(gè)字符串和一個(gè)整型組合倾剿, Int 類型將不會(huì)自動(dòng)轉(zhuǎn)型為 String 類型,這在組合前是非常重要的一步蚌成。
這樣的話前痘,以下代碼會(huì)產(chǎn)生錯(cuò)誤凛捏。
let age = 15
let name = "Robb"
let sentence = name + " is " + age
print(sentence)````
Error: Binary operator '+' cannot be applied to operands
of type 'String' and 'Int'````
因此我們所要做的就是將 age 變成一個(gè) String 類型的〖识龋可以通過強(qiáng)制轉(zhuǎn)型來做到葵袭,使用 String 的初始化方法涵妥,傳入一個(gè) Int 類型的值作為參數(shù)值乖菱。
let age = 15
let name = "Robb"
let stringAge = String(age)
let sentence = name + " is " + stringAge
print(sentence)````
打印結(jié)果為:Robb is 15
我們創(chuàng)建了一個(gè)新的變量叫做 stringAge 。然后使用了類型轉(zhuǎn)換蓬网,因?yàn)樽址逯挡僮鲿?huì)單獨(dú)的分析每一個(gè)表達(dá)式窒所,同樣獲取到圓括號(hào)內(nèi)的內(nèi)容。
let age = 15
let name = "Robb"
let sentence = name + " is " + String(age)
print(sentence)
print("(name) enjoys being (String(age))")````
打印結(jié)果為:
Robb is 15
Robb enjoys being15
可選類型
Swift 中有可選類型的概念帆锋。一個(gè)可選類型是一個(gè)可以為 nil吵取、null 或者是沒有被設(shè)置值的變量。一般來說锯厢,你可以認(rèn)為大部分其他編程語言的任何變量都是一個(gè)可選類型皮官。一個(gè)變量的可選性通過在類型聲明時(shí)的類型名稱后面加上問號(hào)符號(hào) ? 來聲明实辑。 因此繼續(xù)上面的例子捺氢,我們知道 age 和 name 總是會(huì)被設(shè)置,于是我們也許該添加另外一個(gè)可能為 nil 的變量剪撬。我們來拿 favoriteColor 來做一個(gè)例子摄乒。許多人都會(huì)有最愛的顏色,但可能對一部分人來說卻沒有残黑,或者我們不知道別人的那些數(shù)據(jù)馍佑。因此我們會(huì)把它聲明為可選類型,并且不對它進(jìn)行賦值梨水。
var favoriteColor: String?````
在對可選類型的聲明中如果不進(jìn)行賦值拭荤,那么它就是 nil 的。你可以使用 print 函數(shù)來對它進(jìn)行打印來證實(shí)這個(gè)觀點(diǎn)疫诽。
var favoriteColor: String?
print(favoriteColor)````
打印結(jié)果為:nil
我們之后將對 favoriteColor 進(jìn)行賦值穷劈,然后發(fā)現(xiàn)它不再是 nil 的。
var favoriteColor: String?
favoriteColor = "Blue"
print(favoriteColor)
Optional("Blue")````
我們發(fā)現(xiàn)結(jié)果不是 "Blue" 踊沸,而是 Optional("Blue") 歇终。那是因?yàn)閷?shí)際值仍然包裹在可選類型之中。
你可以認(rèn)為可選類型就像一個(gè)生日禮物逼龟,像禮物盒外面那層精美的包裝紙评凝,拆開他們之后,也許里面什么都沒有腺律。這對某些過生日的人真是個(gè)殘忍的禮物奕短,不過這確實(shí)真的會(huì)發(fā)生宜肉。也許禮物盒中確實(shí)會(huì)有真的禮物,可也得拆開并且實(shí)際去看才知道翎碑,現(xiàn)在它只是一個(gè)沒有被拆開谬返,躺在我們手中的一個(gè)盒子。
如果我們想知道里面是什么日杈,需要馬上拆開禮物遣铝,對可選類型來說也是一樣,當(dāng)傳遞和使用它們時(shí)莉擒,實(shí)際我們只是在和一個(gè)也許有值的容器在打交道酿炸。就像禮物一樣,可選類型在被使用之前必須被解包涨冀。
Swift 中聲明一個(gè)可選類型可以不賦值填硕,編譯也會(huì)通過。但是如果我們聲明這些變量時(shí)不加上可選類型的符號(hào)鹿鳖,那么就會(huì)報(bào)錯(cuò)扁眯。
var favoriteColor = "Blue"
favoriteColor = nil````
error: nil cannot be assigned to type 'String'````
同樣,非可選類型在聲明的時(shí)候也不能被賦值為 nil。
```
var favoriteColor: String```
```
error: variables must have an initial value```
####nil
你可以給可選變量賦值為 nil 來表示它沒有值:
var serverResponseCode: Int? = 404
// serverResponseCode 包含一個(gè)可選的 Int 值 404 serverResponseCode = nil
// serverResponseCode 現(xiàn)在不包含值````
注意:nil 不能用于非可選的常量和變量。如果你的代碼中有常量或者變量需要處理值缺失的情況行楞,請把它們聲明成對應(yīng)的可選類型。
如果你聲明一個(gè)可選常量或者變量但是沒有賦值施敢,它們會(huì)自動(dòng)被設(shè)置為 nil :
var surveyAnswer: String?
// surveyAnswer 被自動(dòng)設(shè)置為 nil
注意:Swift 的 nil 和 Objective-C 中的 nil 并不一樣。在 Objective-C 中狭莱, nil 是一個(gè)指向不存在對象的指 針僵娃。在 Swift 中, nil 不是指針——它是一個(gè)確定的值腋妙,用來表示值缺失默怨。任何類型的可選狀態(tài)都可以被設(shè) 置為 nil ,不只是對象類型骤素。
解包
我們現(xiàn)在知道可選類型是什么了匙睹,它們可以使變量可以為空,也知道與其說它們是值不如說是一個(gè)容器济竹。因此痕檬,在項(xiàng)目中要訪問可選類型中的內(nèi)容時(shí),我們該怎么做送浊?
第一梦谜,最普遍的方式是使用可選類型綁定,在可選綁定中,你可以在一個(gè) if 語句中把可選類型的值賦給一個(gè)新的值唁桩。如果可選類型包含一個(gè)值闭树,那個(gè)新的變量就會(huì)被成功設(shè)置,并且跟隨 if 語句的代碼閉包也會(huì)成功執(zhí)行荒澡。
來看例子报辱,這里將聲明兩個(gè)可選類型,一個(gè)叫做 favoriteAnimal 单山,它被設(shè)置值為 Fox 碍现,而另外一個(gè)是 favoriteSong 我們并沒有對它進(jìn)行賦值。
var favoriteAnimal: String?
var favoriteSong: String?
favoriteAnimal = "Fox"```
現(xiàn)在我們使用可選綁定來看一看編程變量是否都有值饥侵,我們可以打印出包含它們值的語句鸵赫。首先先來檢查一下 favoriteAnimal衣屏。
if let unwrappedFavoriteAnimal = favoriteAnimal {
print("Favorite animal is: " + unwrappedFavoriteAnimal)
}
//打印結(jié)果為:Favorite animal is: Fox````
當(dāng)沒有被設(shè)置值時(shí)躏升,僅僅會(huì)觸發(fā) else 語句,或者如果連 else 語句都沒有狼忱,那么什么都不會(huì)觸發(fā)膨疏。
if let unwrappedFavoriteSong = favoriteSong {
print("Favorite song is: " + unwrappedFavoriteSong)
}
else {
print("I don't know what your favorite song is!")
}
//打印結(jié)果為:I don't know what your favorite song is!````
如果我們要解包多個(gè)可選類型,并且對它們進(jìn)行邏輯處理钻弄,首先要檢查它們:
var favoriteAnimal: String?
var favoriteSong: String?
favoriteAnimal = "Fox"
favoriteSong = "Shake it Off"
if let unwrappedFavoriteSong = favoriteSong {
if let unwrappedFavoriteAnimal = favoriteAnimal {
print(unwrappedFavoriteSong + " " + unwrappedFavoriteAnimal)
}
}````
這看上去非常雜亂佃却,因此 Swift 提供一種簡便方式來一次解包多個(gè)變量:
var favoriteAnimal: String?
var favoriteSong: String?
favoriteAnimal = "Fox"
favoriteSong = "Shake it Off"
if let unwrappedFavoriteSong = favoriteSong,
let unwrappedFavoriteAnimal = favoriteAnimal {
print(unwrappedFavoriteSong + " " + unwrappedFavoriteAnimal)
}````
####集合類
Swift 有好幾種集合類型,最常用的是數(shù)組窘俺、集合饲帅、字典。
**數(shù)組**
我們首先來看一下數(shù)組的例子瘤泪。
let starks: [String] = ["Eddard", "Catelyn", "Robb", "Sansa"]````
這里我們定義了一個(gè)基本的 Array 類型灶泵,它是字符串?dāng)?shù)組類型 [String]。
這個(gè)方括號(hào)暗示了它是一個(gè)存放字符串對象的數(shù)組对途,而不是一個(gè)字符串類型赦邻。一般來說,Swift 可以通過檢測所賦的初值進(jìn)行類型推斷实檀。
let starks = ["Robb", "Sansa", "Arya", "Jon"]````
我們可以有多種方式訪問數(shù)組中的元素惶洲,比如通過 Int 類型的下標(biāo),或者調(diào)用各種集合類型的方法膳犹。
let starks = ["Robb", "Sansa", "Arya", "Jon"]
print( starks[0] )
print( starks[2] )
print( starks.first! )````
打印結(jié)果為:
Robb
Arya
Robb
你應(yīng)該發(fā)現(xiàn)數(shù)組是以 0 為下標(biāo)開始的恬吕,因此數(shù)組中的第一個(gè)元素 "Robb" 可以通過 stack[0] 來訪問。
另外须床,可能你會(huì)發(fā)現(xiàn)使用 first 方法返回的是一個(gè)可選值铐料。而下標(biāo)訪問器返回的并不是一個(gè)可選值。如果訪問數(shù)組中沒有出現(xiàn)的下標(biāo),程序?qū)?huì)在運(yùn)行時(shí)報(bào)錯(cuò)余赢。因此在通過下標(biāo)訪問時(shí)檢查數(shù)組的長度:
if starks.count >= 4 {
print( starks[3] )
}````
有幾種方式可以自動(dòng)的檢查這個(gè)類型芯义,但是因?yàn)橐恍┬阅茉蛩粫?huì)默認(rèn)去做。
**哈希類型/字典**
字典可以存儲(chǔ)鍵值對妻柒,鍵的典型類型是字符串類型扛拨,但它也可以是 Swift 中的其他各種類型。在下面這個(gè)例子中举塔,我們會(huì)創(chuàng)建一個(gè)基本字典绑警,以字符串為鍵,整型為值央渣。
```
let ages = ["Robb": 15, "Sansa": 12, "Arya": 10, "Jon": 15]```
我們可以訪問這些值通過 String 的鍵
```
print( ages["Arya"]! )
print( ages["Jon"]! )````
打印結(jié)果為:10 15
要注意的是计盒,我們解包這些值只是因?yàn)樗鼈兪强蛇x值,它們有可能為 nil
使用可選綁定來解包字典中的值是較安全的,特別是你認(rèn)為這些值很有可能為 nil 時(shí)
if let aryasAge = ages["Arya"] {
print("Arya is (aryasAge) years old")
}````
打印結(jié)果為:Arya is 10 years old
我們也可以把數(shù)組存儲(chǔ)在字典中芽丹,或者把字典存儲(chǔ)在數(shù)組中北启,或者把他們混合使用。
let families = [
"Stark": ["Robb": 15, "Sansa": 12, "Arya": 10, "Jon": 15],
"Baratheon": ["Joffrey": 13, "Tommen": 8]
]
let tommensAge = families["Baratheon"]!["Tommen"]!
print("Tommen is \(tommensAge) years old")````
打印結(jié)果為:Tommen is 8 years old
這個(gè) houses 的類型將會(huì)是 [String: [String: Int]]
另外一個(gè)角度也可以說拔第,這是一個(gè)字符串為鍵咕村,以 [String: Int] 為值的一個(gè)字典。
**集合**
Swift3 中的集合和數(shù)組很相似蚊俺,但集合的值是唯一的和無序的懈涛。
初始化一個(gè)集合看起來就像初始化一個(gè)數(shù)組,唯一不同的是類型:
let colors: Set<String> = ["Blue", "Red", "Orange", "Blue"]````
代碼創(chuàng)建了一個(gè)字符串的集合泳猬。大于和小于符號(hào) "<"">" 暗示 Swift 中的泛型類型批钠,你可能注意到了 "Blue" 在列表中出現(xiàn)了兩次,但是如果我們把顏色打印出來得封,馬上就會(huì)發(fā)現(xiàn):
let colors: Set<String> = ["Blue", "Red", "Orange", "Blue"]
print(colors)
///打印結(jié)果:["Orange", "Red", "Blue"]````
你也許還注意到了順序也不一致了埋心,因?yàn)榧喜粫?huì)維持特定的順序。
我們無法像訪問數(shù)組下標(biāo)一樣的方式去訪問集合呛每。但是可以用集合中內(nèi)置的方法來增加或者刪除元素踩窖,可以通過 contains 方法來查看是否集合中包含了該元素。
var colors: Set<String> = ["Blue", "Red", "Orange", "Blue"]
colors.insert("Black")
colors.insert("Black")
colors.remove("Red")
print(colors)
print(colors.contains("Black"))
print(colors.contains("Red"))
["Black", "Orange", "Blue"]
//true
//false````
構(gòu)造集合對象最常見的方式就是羅列哪些元素應(yīng)該納入列表晨横,哪些元素應(yīng)該被排除洋腮。
這里還有許多方法我還沒有提到,我建議你去閱讀一下蘋果的官方文檔關(guān)于這三種集合類型手形,這樣就會(huì)對它們更了解啥供。
元組
元組并不是一種集合,而應(yīng)該說是用一個(gè)標(biāo)識(shí)符來表示多個(gè)不同變量库糠。
let fullName = ("Jameson", "Quave")````
(String, String) 是一個(gè)元組類型伙狐,我們可以使用點(diǎn)語法來訪問每一個(gè)元組的成員涮毫,看看下面的情況:
let fullName = ("Jameson", "Quave")
print(fullName.1)
print(fullName.0)
//Quave
//Jameson````
元組也可以用一個(gè)新的多個(gè)變量名來構(gòu)造:
let (first, last) = ("Jameson", "Quave")
print(first)
//Jameson````
由于我們沒有用到 last name,可以忽略那個(gè)值通過使用下劃線 _ 贷屎,并且仍然構(gòu)造 first name罢防。
let (first, _) = ("Jameson", "Quave")
print(first)
Jameson````
當(dāng)你在使用方法時(shí)想返回多個(gè)返回值時(shí),元組會(huì)很有用唉侄。
控制流
Swift 的控制流比起其他語言要優(yōu)雅咒吐,我們先從 if 和 else 語句這些基本層面著手:
if 10 > 5 {
print("10 is greater than 5.")
}
else {
print("10 is not greater than five.")
}
//10 is greater than 5```
你也可以用括號(hào)來包裹 if 語句的條件:
if (10 > 5) {
...
//Swift 也支持 switch 語句,在編譯期的時(shí)候檢查你是否已經(jīng)覆蓋了所有的可能條件属划,
//如果你沒有覆蓋所有的條件恬叹,你得加上 defualt:case 來處理一些沒有考慮到的情況:
let name = "Jameson"
switch(name) {
case "Joe":
print("Name is Joe")
case "Jameson":
print("This is Jameson")
default:
print("I don't know of this person!")
}
//This is Jameson```
由于此處 name 的值是 "This is Jameson"。我們匹配到了第二個(gè)條件同眯,然后執(zhí)行下面這行绽昼。
print("This is Jameson")
如果我們把名稱設(shè)置為一些之前沒有出現(xiàn)在列舉情況的東西時(shí),比如 "Jason" 须蜗,switch 將會(huì)自動(dòng)落入默認(rèn)的情況:
let name = "Jason"
switch(name) {
case "Joe":
print("Name is Joe")
case "Jameson":
print("This is Jameson")
default:
print("I don't know of this person!")
}
//I don't know of this person!```
####循環(huán)和集合類型
Swift3 不再支持你過去所使用的 C 風(fēng)格的循環(huán)硅确,取而代之的是使用枚舉和 for-each 風(fēng)格的循環(huán),語法是` for element in array`
let names = ["Robb", "Sansa", "Arya", "Jon"]
for name in names {
print("Name: (name)")
}
Name: Robb
Name: Sansa
Name: Arya
Name: Jon```
如果你想要循環(huán)整個(gè)數(shù)組唠粥,這個(gè)寫法就很棒疏魏,沒有 C 風(fēng)格的數(shù)組停做,如果我們循環(huán)遍歷一系列數(shù)字呢晤愧?Swift 中的 Range 和 Stride 給出了答案,如果想要打印到 10 里面3的倍數(shù)蛉腌,可以使用 Range 通過使用語法 1…10 表示從 1 到 10 官份。然后我們打印每一個(gè)數(shù)字,那些數(shù)字都被 % 符號(hào)除以 3 烙丛,并且檢查它們的余數(shù)是不是都是0舅巷。
for i in 1...10 {
if i % 3 == 0 {
print(i)
}
}```
輸出結(jié)果為:3 6 9
另外一種方式是通過 stride 每隔三個(gè)元素訪問一次。stride 可以用很多方法來創(chuàng)建河咽,但是最常見的是 stride(from: , to:, by:)钠右,from value 就是跨步訪問的起初值,然后 by 是每隔多少跨步值才能訪問到 to 值忘蟹。聽起來有點(diǎn)繞飒房,讓我們來看實(shí)際的代碼:
let byThrees = stride(from: 3, to: 10, by: 3)
for n in byThrees {
print(n)
}```
輸出結(jié)果為:3 6 9
從英語來看十分好讀,你也可以說你從 3 數(shù)到 10 每隔3個(gè)數(shù)媚值。這里我們創(chuàng)造 stride 并且用一個(gè)變量 byThrees 來存儲(chǔ)他們的值狠毯,但是也可以直接在循環(huán)中使用它們。
for n in stride(from: 3, to: 10, by: 3) {
print(n)
}````
//輸出結(jié)果為3 6 9
集合都有一個(gè) indices 屬性用于循環(huán)中使用褥芒,它會(huì)返回一個(gè)集合的下標(biāo)數(shù)組嚼松,非常適合訪問或者過濾集合中某些元素的情況。這里回到我們的名稱集合的例子,如果想要前三個(gè)名稱献酗,可以這樣寫:
let names = ["Robb", "Sansa", "Arya", "Jon"]
for nameIndex in names.indices {
if(nameIndex < 3) {
print(names[nameIndex])
}
}
Robb, Sansa, Arya````
在集合中還有枚舉的方法寝受,它允許你通過遍歷下標(biāo)和值:
let names = ["Robb", "Sansa", "Arya", "Jon"]
for (index, name) in names.enumerated() {
print("\(index): \(name)")
}
0: Robb
1: Sansa
2: Arya
3: Jon````
在 Swift3 中還有很多方式來遍歷對象,但他們通常不是很常用罕偎。
也許你已經(jīng)發(fā)現(xiàn)我們的循環(huán)中同時(shí)給兩個(gè)變量賦值羡蛾,index 和 name。它們被用逗號(hào)分隔并被括號(hào)括起來锨亏,表示我們從 enumerated() 返回的兩個(gè)被命名的變量痴怨。
####函數(shù)和閉包
使用`func`來聲明函數(shù),使用名字和參數(shù)來調(diào)用函數(shù)器予。使用->來指定函數(shù)返回值的類型浪藻。
//聲明creat
方法,傳入person
和day
兩個(gè)參數(shù)乾翔,返回一個(gè)String
類型的值
func creat(person: String, day: String) -> String {
return "Hello,(person),today is (day)."
}
//使用creat
函數(shù)調(diào)用生成一個(gè)字符串爱葵,并賦值給result
let result = creat(person: "小王", day: "星期八")
默認(rèn)情況下,函數(shù)使用他們的參數(shù)作為他們的參數(shù)的標(biāo)簽反浓,在參數(shù)名稱前可以自定義參數(shù)標(biāo)簽萌丈,或者使用\`_`表示不適用參數(shù)標(biāo)簽。
//聲明函數(shù)
func creat1(_ person: String, day: String) -> String {
return "Hello,(person),today is (day)."
}
//調(diào)用方式
let result1 = creat1( "小王", day: "星期八")
print(result1);
使用元組來讓一個(gè)函數(shù)返回多個(gè)值雷则,元組的元素可以用名稱和數(shù)字來表示辆雾。
//聲明一個(gè)函數(shù)
func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
var min = scores[0]
var max = scores[0]
var sum = 0
for score in scores {
if score > max {
max = score
} else if score < min {
min = score
}
sum += score
}
return (min, max, sum)
}
//調(diào)用函數(shù)
let statistics = calculateStatistics(scores:[5, 3, 100, 3, 9])
print(statistics.sum)
print(statistics.2)
函數(shù)可以嵌套。被嵌套的函數(shù)可以訪問外側(cè)函數(shù)的變量月劈,你可以使用嵌套函數(shù)來重構(gòu)一個(gè)太長或者太復(fù)雜的函
數(shù)度迂。
func returnFifteen() -> Int {
var y = 10
func add() {
y += 5
}
add()
return y
}
returnFifteen()
函數(shù)是第一等類型,這意味著函數(shù)可以作為另一個(gè)函數(shù)的返回值猜揪。
func makeIncrementer() -> ((Int) -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)
函數(shù)也可以當(dāng)做參數(shù)傳入另一個(gè)函數(shù)惭墓。
func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
} }
return false
}
func lessThanTen(number: Int) -> Bool {
return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(list: numbers, condition: lessThanTen)
函數(shù)實(shí)際上是一種特殊的閉包:它是一段能之后被調(diào)取的代碼。閉包中的代碼能訪問閉包所建作用域中能得到的變 量和函數(shù)而姐,即使閉包是在一個(gè)不同的作用域被執(zhí)行的 - 你已經(jīng)在嵌套函數(shù)例子中所看到腊凶。你可以使用 {} 來創(chuàng)建 一個(gè)匿名閉包。使用` in `將參數(shù)和返回值類型聲明與閉包函數(shù)體進(jìn)行分離拴念。
numbers.map({
(number: Int) -> Int in
let result = 3 * number
return result
})````
對象和類
使用 class 和類名來創(chuàng)建一個(gè)類钧萍。類中屬性的聲明和常量、變量聲明一樣丈莺,唯一的區(qū)別就是它們的上下文是 類划煮。同樣,方法和函數(shù)聲明也一樣缔俄。
class Shape {
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
要?jiǎng)?chuàng)建一個(gè)類的實(shí)例弛秋,在類名后面加上括號(hào)器躏。使用點(diǎn)語法來訪問實(shí)例的屬性和方法。
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
這個(gè)版本的 Shape 類
缺少了一些重要的東西:一個(gè)構(gòu)造函數(shù)來初始化類實(shí)例蟹略。使用 init 來創(chuàng)建一個(gè)構(gòu)造器登失。
class NamedShape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
注意 self
被用來區(qū)別實(shí)例變量。當(dāng)你創(chuàng)建實(shí)例的時(shí)候挖炬,像傳入函數(shù)參數(shù)一樣給類傳入構(gòu)造器的參數(shù)揽浙。每個(gè)屬性都 需要賦值——無論是通過聲明(就像 numberOfSides )還是通過構(gòu)造器(就像 name )。
如果你需要在刪除對象之前進(jìn)行一些清理工作意敛,使用 deinit
創(chuàng)建一個(gè)析構(gòu)函數(shù)馅巷。
子類的定義方法是在它們的類名后面加上父類的名字,用冒號(hào)分割草姻。創(chuàng)建類的時(shí)候并不需要一個(gè)標(biāo)準(zhǔn)的根類钓猬,所以你可以忽略父類。
子類如果要重寫父類的方法的話撩独,需要用 override 標(biāo)記——如果沒有添加 override 就重寫父類方法的話編譯器 會(huì)報(bào)錯(cuò)敞曹。編譯器同樣會(huì)檢測 override 標(biāo)記的方法是否確實(shí)在父類中。
class Square: NamedShape {
var sideLength: Double
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 4
}
func area() -> Double {
return sideLength * sideLength
}
override func simpleDescription() -> String {
return "A square with sides of length \(sideLength)."
} }
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()
除了儲(chǔ)存簡單的屬性之外综膀,屬性可以有 getter 和 setter 澳迫。
class EquilateralTriangle: NamedShape {
var sideLength: Double = 0.0
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 3
}
var perimeter: Double {
get {
return 3.0 * sideLength
}
set {
sideLength = newValue / 3.0
} }
override func simpleDescription() -> String {
return "An equilateral triagle with sides of length \(sideLength)."
} }
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter)
triangle.perimeter = 9.9
print(triangle.sideLength)
在 perimeter 的 setter 中,新值的名字是 newValue 剧劝。你可以在 set 之后顯式的設(shè)置一個(gè)名字橄登。 注意 EquilateralTriangle 類的構(gòu)造器執(zhí)行了三步:
- 設(shè)置子類聲明的屬性值
- 調(diào)用父類的構(gòu)造器
- 改變父類定義的屬性值。其他的工作比如調(diào)用方法getters 和 setters 也可以在這個(gè)階段完成担平。
如果你不需要計(jì)算屬性示绊,但是仍然需要在設(shè)置一個(gè)新值之前或者之后運(yùn)行代碼,使用 willSet 和 didSet 暂论。 比如,下面的類確保三角形的邊長總是和正方形的邊長相同拌禾。
class TriangleAndSquare {
var triangle: EquilateralTriangle {
willSet {
square.sideLength = newValue.sideLength
} }
var square: Square {
willSet {
triangle.sideLength = newValue.sideLength
}
}
init(size: Double, name: String) {
square = Square(sideLength: size, name: name)
triangle = EquilateralTriangle(sideLength: size, name: name)
}
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
print(triangleAndSquare.square.sideLength)
print(triangleAndSquare.triangle.sideLength)
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)
處理變量的可選值時(shí)取胎,你可以在操作(比如方法、屬性和子腳本)之前加 ? 湃窍。如果 ? 之前的值是 nil 闻蛀, ? 后面 的東西都會(huì)被忽略,并且整個(gè)表達(dá)式返回 nil 您市。否則觉痛, ? 之后的東西都會(huì)被運(yùn)行。在這兩種情況下茵休,整個(gè)表達(dá)式 的值也是一個(gè)可選值薪棒。
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
枚舉和結(jié)構(gòu)體
使用 enum 來創(chuàng)建一個(gè)枚舉手蝎。就像類和其他所有命名類型一樣,枚舉可以包含方法俐芯。
enum Rank: Int {
case Ace = 1
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King
func simpleDescription() -> String {
switch self {
case .Ace:
return "ace"
case .Jack:
return "jack"
case .Queen:
return "queen"
case .King:
return "king"
default:
return String(self.rawValue)
}
} }
let ace = Rank.Ace
let aceRawValue = ace.rawValue
默認(rèn)情況下棵介,Swift 按照從 0 開始每次加 1 的方式為原始值進(jìn)行賦值,不過你可以通過顯式賦值進(jìn)行改變
使用init?(rawValue:)
初始化構(gòu)造器在原始值和枚舉值之間進(jìn)行轉(zhuǎn)換吧史。
if let convertedRank = Rank(rawValue: 3) {
let threeDescription = convertedRank.simpleDescription()
}
枚舉的成員值是實(shí)際值邮辽,并不是原始值的另一種表達(dá)方法。實(shí)際上贸营,如果沒有比較有意義的原始值吨述,你就不需要
提供原始值。
enum Suit {
case Spades, Hearts, Diamonds, Clubs
func simpleDescription() -> String {
switch self {
case .Spades:
return "spades"
case .Hearts:
return "hearts"
case .Diamonds:
return "diamonds"
case .Clubs:
return "clubs"
}
} }
let hearts = Suit.Hearts
let heartsDescription = hearts.simpleDescription()
注意钞脂,有兩種方式可以引用 Hearts 成員:給 hearts 常量賦時(shí)锐极,枚舉成員 Suit.Hearts 需要用全名來引用,因 為常量沒有顯式指定類型芳肌。在 switch 里灵再,枚舉成員使用縮寫 .Hearts 來引用,因?yàn)?self 的值已經(jīng)知道是一個(gè) it 亿笤。已知變量類型的情況下你可以使用縮寫翎迁。
一個(gè)枚舉成員的實(shí)例可以有實(shí)例值。相同枚舉成員的實(shí)例可以有不同的值净薛。創(chuàng)建實(shí)例的時(shí)候傳入值即可汪榔。實(shí)例值和原始值是不同的:枚舉成員的原始值對于所有實(shí)例都是相同的,而且你是在定義枚舉的時(shí)候設(shè)置原始值肃拜。
使用 struct 來創(chuàng)建一個(gè)結(jié)構(gòu)體痴腌。結(jié)構(gòu)體和類有很多相同的地方,比如方法和構(gòu)造器燃领。它們之間最大的一個(gè)區(qū)別就 是結(jié)構(gòu)體是傳值士聪,類是傳引用。
struct Card {
var rank: Rank
var suit: Suit
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
協(xié)議和擴(kuò)展
使用 protocol 來聲明一個(gè)協(xié)議猛蔽。
protocol ExampleProtocol {
var simpleDescription: String { get }
mutating func adjust()
}
類剥悟、枚舉和結(jié)構(gòu)體都可以實(shí)現(xiàn)協(xié)議。
class SimpleClass: ExampleProtocol {
var simpleDescription: String = "A very simple class."
var anotherProperty: Int = 69105
func adjust() {
simpleDescription += " Now 100% adjusted."
}
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription
struct SimpleStructure: ExampleProtocol {
var simpleDescription: String = "A simple structure"
mutating func adjust() {
simpleDescription += " (adjusted)"
}
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription
注意聲明 SimpleStructure 時(shí)候 mutating 關(guān)鍵字用來標(biāo)記一個(gè)會(huì)修改結(jié)構(gòu)體的方法曼库。 SimpleClass 的聲明不需要 標(biāo)記任何方法区岗,因?yàn)轭愔械姆椒ㄍǔ?梢孕薷念悓傩?類的性質(zhì))毁枯。
使用 extension 來為現(xiàn)有的類型添加功能慈缔,比如新的方法和計(jì)算屬性。你可以使用擴(kuò)展在別處修改定義种玛,甚至是 從外部庫或者框架引入的一個(gè)類型藐鹤,使得這個(gè)類型遵循某個(gè)協(xié)議瓤檐。
extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 42 }
}
print(7.simpleDescription)
你可以像使用其他命名類型一樣使用協(xié)議名——例如,創(chuàng)建一個(gè)有不同類型但是都實(shí)現(xiàn)一個(gè)協(xié)議的對象集合教藻。當(dāng)
你處理類型是協(xié)議的值時(shí)距帅,協(xié)議外定義的方法不可用。
let protocolValue: ExampleProtocol = a print(protocolValue.simpleDescription)
// print(protocolValue.anotherProperty) // 去掉注釋可以看到錯(cuò)誤
即使 protocolValue 變量運(yùn)行時(shí)的類型是 simpleClass 括堤,編譯器會(huì)把它的類型當(dāng)做 ExampleProtocol 碌秸。這表示你不 能調(diào)用類在它實(shí)現(xiàn)的協(xié)議之外實(shí)現(xiàn)的方法或者屬性。
錯(cuò)誤處理
使用采用 Error
協(xié)議的類型來表示錯(cuò)誤悄窃。
enum PrinterError: Error {
case OutOfPaper
case NoToner
case OnFire
}
使用throw
來拋出一個(gè)錯(cuò)誤并使用 throws
來表示一個(gè)可以拋出錯(cuò)誤的函數(shù)讥电。如果在函數(shù)中拋出一個(gè)錯(cuò)誤,這個(gè)函 數(shù)會(huì)立刻返回并且調(diào)用該函數(shù)的代碼會(huì)進(jìn)行錯(cuò)誤處理轧抗。
func send(job: Int, toPrinter printerName: String) throws -> String {
if printerName == "Never Has Toner" {
throw PrinterError.noToner
}
return "Job sent"
}
有多種方式可以用來進(jìn)行錯(cuò)誤處理恩敌。一種方式是使用 do-catch 。在 do 代碼塊中横媚,使用 try 來標(biāo)記可以拋出錯(cuò)誤 的代碼纠炮。在 catch 代碼塊中,除非你另外命名灯蝴,否則錯(cuò)誤會(huì)自動(dòng)命名為 error 恢口。
do {
let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
print(printerResponse)
} catch {
print(error)
}
可以使用多個(gè) catch 塊來處理特定的錯(cuò)誤。參照 switch 中的 case 風(fēng)格來寫 catch 穷躁。
do {
let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
print(printerResponse)
} catch PrinterError.onFire {
print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
print("Printer error: \(printerError).")
} catch {
print(error)
}
另一種處理錯(cuò)誤的方式使用try?
將結(jié)果轉(zhuǎn)換為可選的耕肩。如果函數(shù)拋出錯(cuò)誤,該錯(cuò)誤會(huì)被拋棄并且結(jié)果為nil
问潭。否則的話猿诸,結(jié)果會(huì)是一個(gè)包含函數(shù)返回值的可選值。
let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")
使用 defer
代碼塊來表示在函數(shù)返回前狡忙,函數(shù)中最后執(zhí)行的代碼梳虽。無論函數(shù)是否會(huì)拋出錯(cuò)誤,這段代碼都將執(zhí) 行去枷。使用defer
怖辆,可以把函數(shù)調(diào)用之初就要執(zhí)行的代碼和函數(shù)調(diào)用結(jié)束時(shí)的掃尾代碼寫在一起,雖然這兩者的執(zhí) 行時(shí)機(jī)截然不同删顶。
var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]
func fridgeContains(_ food: String) -> Bool {
fridgeIsOpen = true
defer {
fridgeIsOpen = false
}
let result = fridgeContent.contains(food)
return result
}
fridgeContains("banana")
print(fridgeIsOpen)
泛型
在尖括號(hào)里寫一個(gè)名字來創(chuàng)建一個(gè)泛型函數(shù)或者類型。
func repeatItem<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
var result = [Item]()
for _ in 0..<numberOfTimes {
result.append(item)
}
return result
}
repeatItem(repeating: "knock", numberOfTimes:4)
你也可以創(chuàng)建泛型函數(shù)淑廊、方法逗余、類、枚舉和結(jié)構(gòu)體季惩。
// 重新實(shí)現(xiàn) Swift 標(biāo)準(zhǔn)庫中的可選類型 enum OptionalValue<Wrapped> {
case None
case Some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .None
possibleInteger = .Some(100)
在類型名后面使用 where 來指定對類型的需求录粱,比如腻格,限定類型實(shí)現(xiàn)某一個(gè)協(xié)議,限定兩個(gè)類型是相同的啥繁,或者 限定某個(gè)類必須有一個(gè)特定的父類菜职。
func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
where T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element {
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
return true
} }
}
return false
}
anyCommonElements([1, 2, 3], [3])
<T: Equatable> <T> ... where T: Equatable>是等價(jià)的。