Swift基礎(chǔ)知識(shí)

變量和常量

任何 Swift 中的變量要么不變的,要么是可變的。這句話可不要和 Int、 Float 這些變量類型混淆。變量和常量?jī)H僅是一種視角來(lái)描繪持有的值是可修改的(即可變性),亦或是不能修改的(即不可變性)。
要定義一個(gè)常量,使用 let 關(guān)鍵字。舉例來(lái)說(shuō):

let name = "Jameson"````
如果你想改變 name 的值,會(huì)發(fā)現(xiàn)沒(méi)有辦法做到,然后 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)鍵字,來(lái)定義一個(gè)可以修改的變量:

var name = "Jameson"
name = "Bob"````
這次代碼就不會(huì)報(bào)錯(cuò)了。
一般來(lái)說(shuō),你應(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)鍵字?;蛘呷珏e(cuò)誤是給你的提示,應(yīng)該重新去思考當(dāng)前的邏輯流。一般來(lái)說(shuō),不可變性優(yōu)于可變性,它可以幫助開(kāi)發(fā)者少犯錯(cuò)誤,并且更容易編寫(xiě)代碼。

分號(hào)

在Swift 并不強(qiáng)制要求你在每條語(yǔ)句的結(jié)尾處使用分號(hào)( ; ),當(dāng)然,你也可以按照 你自己的習(xí)慣添加分號(hào)。有一種情況下必須要用分號(hào),即你打算在同一行內(nèi)寫(xiě)多條獨(dú)立的語(yǔ)句:

let cat = "?"; print(cat) 
// 輸出 "?"

注釋

//單行注釋
/*
多行注釋
*/````
####類型推斷
在Swift中,可以不用顯式的制定數(shù)據(jù)類型,swift能夠自行推測(cè)出數(shù)據(jù)類型

####斷言
斷言在調(diào)試中非常有用,就像別的語(yǔ)言中的斷點(diǎn)調(diào)試。Swift通過(guò)一個(gè)全局函數(shù)assert來(lái)實(shí)現(xiàn)。它有兩個(gè)參數(shù):一個(gè)表達(dá)式,一條信息,若表達(dá)式的結(jié)果是false就會(huì)中斷程序并打印那條信息。
```
var age = 34
assert(age<18, "如果你小于18歲你就不是大叔了")
```
####基礎(chǔ)類型
在 Swift 中,一個(gè)類型被聲明的寫(xiě)法是通過(guò)聲明一個(gè)變量,然后緊跟一個(gè)冒號(hào),然后是類型名稱。例如我們聲明一個(gè)整型,在 Swift 中類型是 Int ,那么你可以如下寫(xiě)法:

let age: Int = 5````
相似的,你可以聲明一個(gè)字符串類型:

let name: String = "Jameson"````
Swift 支持類型推斷,可以不寫(xiě)具體的類型信息。然后讓編譯器根據(jù)它的初始值來(lái)推斷它是什么類型的。

let age = 5
let name = "Jameson"````
age 和 name 的類型仍然是 Int 和 String ,但是這次我們跳過(guò)了類型聲明,因?yàn)楹茱@然,5 是 Int 類型,而 “Jameson” 是一個(gè)字符串。
記住, let 關(guān)鍵字僅僅使值變得不可變。如果我們預(yù)測(cè)這個(gè) age 的值是可變的,而 name 不是可變的,那么我們應(yīng)該這么寫(xiě):

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 的語(yǔ)句,可以在兩個(gè) String 變量之間用 + 操作符。

let age = "15"
let name = "Robb"
 
let sentence = name + " is " + age
print(sentence)````
打印結(jié)果為:Robb is 15
改用另外一個(gè)方式來(lái)拼接 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,沒(méi)有了引號(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 類型的。可以通過(guò)強(qiáng)制轉(zhuǎn)型來(lái)做到,使用 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 或者是沒(méi)有被設(shè)置值的變量。一般來(lái)說(shuō),你可以認(rèn)為大部分其他編程語(yǔ)言的任何變量都是一個(gè)可選類型。一個(gè)變量的可選性通過(guò)在類型聲明時(shí)的類型名稱后面加上問(wèn)號(hào)符號(hào) ? 來(lái)聲明。 因此繼續(xù)上面的例子,我們知道 age 和 name 總是會(huì)被設(shè)置,于是我們也許該添加另外一個(gè)可能為 nil 的變量。我們來(lái)拿 favoriteColor 來(lái)做一個(gè)例子。許多人都會(huì)有最愛(ài)的顏色,但可能對(duì)一部分人來(lái)說(shuō)卻沒(méi)有,或者我們不知道別人的那些數(shù)據(jù)。因此我們會(huì)把它聲明為可選類型,并且不對(duì)它進(jìn)行賦值。

var favoriteColor: String?````
在對(duì)可選類型的聲明中如果不進(jìn)行賦值,那么它就是 nil 的。你可以使用 print 函數(shù)來(lái)對(duì)它進(jìn)行打印來(lái)證實(shí)這個(gè)觀點(diǎn)。

var favoriteColor: String?
print(favoriteColor)````
打印結(jié)果為:nil
我們之后將對(duì) 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è)生日禮物,像禮物盒外面那層精美的包裝紙,拆開(kāi)他們之后,也許里面什么都沒(méi)有。這對(duì)某些過(guò)生日的人真是個(gè)殘忍的禮物,不過(guò)這確實(shí)真的會(huì)發(fā)生。也許禮物盒中確實(shí)會(huì)有真的禮物,可也得拆開(kāi)并且實(shí)際去看才知道,現(xiàn)在它只是一個(gè)沒(méi)有被拆開(kāi),躺在我們手中的一個(gè)盒子。
如果我們想知道里面是什么,需要馬上拆開(kāi)禮物,對(duì)可選類型來(lái)說(shuō)也是一樣,當(dāng)傳遞和使用它們時(shí),實(shí)際我們只是在和一個(gè)也許有值的容器在打交道。就像禮物一樣,可選類型在被使用之前必須被解包。
Swift 中聲明一個(gè)可選類型可以不賦值,編譯也會(huì)通過(guò)。但是如果我們聲明這些變量時(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 來(lái)表示它沒(méi)有值:

var serverResponseCode: Int? = 404
// serverResponseCode 包含一個(gè)可選的 Int 值 404 serverResponseCode = nil
// serverResponseCode 現(xiàn)在不包含值````
注意:nil 不能用于非可選的常量和變量。如果你的代碼中有常量或者變量需要處理值缺失的情況,請(qǐng)把它們聲明成對(duì)應(yīng)的可選類型。
如果你聲明一個(gè)可選常量或者變量但是沒(méi)有賦值,它們會(huì)自動(dòng)被設(shè)置為 nil :

var surveyAnswer: String?
// surveyAnswer 被自動(dòng)設(shè)置為 nil

注意:Swift 的 nil 和 Objective-C 中的 nil 并不一樣。在 Objective-C 中, nil 是一個(gè)指向不存在對(duì)象的指 針。在 Swift 中, nil 不是指針——它是一個(gè)確定的值,用來(lái)表示值缺失。任何類型的可選狀態(tài)都可以被設(shè) 置為 nil ,不只是對(duì)象類型。

解包

我們現(xiàn)在知道可選類型是什么了,它們可以使變量可以為空,也知道與其說(shuō)它們是值不如說(shuō)是一個(gè)容器。因此,在項(xiàng)目中要訪問(wèn)可選類型中的內(nèi)容時(shí),我們?cè)撛趺醋觯?br> 第一,最普遍的方式是使用可選類型綁定,在可選綁定中,你可以在一個(gè) if 語(yǔ)句中把可選類型的值賦給一個(gè)新的值。如果可選類型包含一個(gè)值,那個(gè)新的變量就會(huì)被成功設(shè)置,并且跟隨 if 語(yǔ)句的代碼閉包也會(huì)成功執(zhí)行。
來(lái)看例子,這里將聲明兩個(gè)可選類型,一個(gè)叫做 favoriteAnimal ,它被設(shè)置值為 Fox ,而另外一個(gè)是 favoriteSong 我們并沒(méi)有對(duì)它進(jìn)行賦值。

var favoriteAnimal: String?
var favoriteSong: String?
 
favoriteAnimal = "Fox"```
現(xiàn)在我們使用可選綁定來(lái)看一看編程變量是否都有值,我們可以打印出包含它們值的語(yǔ)句。首先先來(lái)檢查一下 favoriteAnimal。

if let unwrappedFavoriteAnimal = favoriteAnimal {
print("Favorite animal is: " + unwrappedFavoriteAnimal)
}
//打印結(jié)果為:Favorite animal is: Fox````
當(dāng)沒(méi)有被設(shè)置值時(shí),僅僅會(huì)觸發(fā) else 語(yǔ)句,或者如果連 else 語(yǔ)句都沒(méi)有,那么什么都不會(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è)可選類型,并且對(duì)它們進(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 提供一種簡(jiǎn)便方式來(lái)一次解包多個(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ù)組**
我們首先來(lái)看一下數(shù)組的例子。

let starks: [String] = ["Eddard", "Catelyn", "Robb", "Sansa"]````
這里我們定義了一個(gè)基本的 Array 類型,它是字符串?dāng)?shù)組類型 [String]。
這個(gè)方括號(hào)暗示了它是一個(gè)存放字符串對(duì)象的數(shù)組,而不是一個(gè)字符串類型。一般來(lái)說(shuō),Swift 可以通過(guò)檢測(cè)所賦的初值進(jìn)行類型推斷。

let starks = ["Robb", "Sansa", "Arya", "Jon"]````
我們可以有多種方式訪問(wèn)數(shù)組中的元素,比如通過(guò) 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)開(kāi)始的,因此數(shù)組中的第一個(gè)元素 "Robb" 可以通過(guò) stack[0] 來(lái)訪問(wèn)。
另外,可能你會(huì)發(fā)現(xiàn)使用 first 方法返回的是一個(gè)可選值。而下標(biāo)訪問(wèn)器返回的并不是一個(gè)可選值。如果訪問(wèn)數(shù)組中沒(méi)有出現(xiàn)的下標(biāo),程序?qū)?huì)在運(yùn)行時(shí)報(bào)錯(cuò)。因此在通過(guò)下標(biāo)訪問(wèn)時(shí)檢查數(shù)組的長(zhǎng)度:

if starks.count >= 4 {
    print( starks[3] )
}````
有幾種方式可以自動(dòng)的檢查這個(gè)類型,但是因?yàn)橐恍┬阅茉蛩粫?huì)默認(rèn)去做。
**哈希類型/字典**
字典可以存儲(chǔ)鍵值對(duì),鍵的典型類型是字符串類型,但它也可以是 Swift 中的其他各種類型。在下面這個(gè)例子中,我們會(huì)創(chuàng)建一個(gè)基本字典,以字符串為鍵,整型為值。
```
let ages = ["Robb": 15, "Sansa": 12, "Arya": 10, "Jon": 15]```
我們可以訪問(wèn)這些值通過(guò) String 的鍵
```
print( ages["Arya"]! )
print( ages["Jon"]! )````
打印結(jié)果為:10  15
要注意的是,我們解包這些值只是因?yàn)樗鼈兪强蛇x值,它們有可能為 nil
使用可選綁定來(lái)解包字典中的值是較安全的,特別是你認(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è)角度也可以說(shuō),這是一個(gè)字符串為鍵,以 [String: Int] 為值的一個(gè)字典。
**集合**
Swift3 中的集合和數(shù)組很相似,但集合的值是唯一的和無(wú)序的。
初始化一個(gè)集合看起來(lái)就像初始化一個(gè)數(shù)組,唯一不同的是類型:

let colors: Set<String> = ["Blue", "Red", "Orange", "Blue"]````
代碼創(chuàng)建了一個(gè)字符串的集合。大于和小于符號(hào) "<"">" 暗示 Swift 中的泛型類型,你可能注意到了 "Blue" 在列表中出現(xiàn)了兩次,但是如果我們把顏色打印出來(lái),馬上就會(huì)發(fā)現(xiàn):

let colors: Set<String> = ["Blue", "Red", "Orange", "Blue"]
print(colors)
///打印結(jié)果:["Orange", "Red", "Blue"]````
你也許還注意到了順序也不一致了,因?yàn)榧喜粫?huì)維持特定的順序。
我們無(wú)法像訪問(wèn)數(shù)組下標(biāo)一樣的方式去訪問(wèn)集合。但是可以用集合中內(nèi)置的方法來(lái)增加或者刪除元素,可以通過(guò) contains 方法來(lái)查看是否集合中包含了該元素。

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)造集合對(duì)象最常見(jiàn)的方式就是羅列哪些元素應(yīng)該納入列表,哪些元素應(yīng)該被排除。
這里還有許多方法我還沒(méi)有提到,我建議你去閱讀一下蘋(píng)果的官方文檔關(guān)于這三種集合類型,這樣就會(huì)對(duì)它們更了解。
元組
元組并不是一種集合,而應(yīng)該說(shuō)是用一個(gè)標(biāo)識(shí)符來(lái)表示多個(gè)不同變量。

let fullName = ("Jameson", "Quave")````
(String, String) 是一個(gè)元組類型,我們可以使用點(diǎn)語(yǔ)法來(lái)訪問(wèn)每一個(gè)元組的成員,看看下面的情況:

let fullName = ("Jameson", "Quave")
print(fullName.1)
print(fullName.0)
//Quave
//Jameson````
元組也可以用一個(gè)新的多個(gè)變量名來(lái)構(gòu)造:

let (first, last) = ("Jameson", "Quave")
print(first)
//Jameson````
由于我們沒(méi)有用到 last name,可以忽略那個(gè)值通過(guò)使用下劃線 _ ,并且仍然構(gòu)造 first name。

let (first, _) = ("Jameson", "Quave")
print(first)
Jameson````
當(dāng)你在使用方法時(shí)想返回多個(gè)返回值時(shí),元組會(huì)很有用。

控制流

Swift 的控制流比起其他語(yǔ)言要優(yōu)雅,我們先從 if 和 else 語(yǔ)句這些基本層面著手:

if 10 > 5 {
  print("10 is greater than 5.")
}
else {
    print("10 is not greater than five.")
}
//10 is greater than 5```
你也可以用括號(hào)來(lái)包裹 if 語(yǔ)句的條件:

if (10 > 5) {
...
//Swift 也支持 switch 語(yǔ)句,在編譯期的時(shí)候檢查你是否已經(jīng)覆蓋了所有的可能條件,
//如果你沒(méi)有覆蓋所有的條件,你得加上 defualt:case 來(lái)處理一些沒(méi)有考慮到的情況:
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è)置為一些之前沒(méi)有出現(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 不再支持你過(guò)去所使用的 C 風(fēng)格的循環(huán),取而代之的是使用枚舉和 for-each 風(fēng)格的循環(huán),語(yǔ)法是` 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è)寫(xiě)法就很棒,沒(méi)有 C 風(fēng)格的數(shù)組,如果我們循環(huán)遍歷一系列數(shù)字呢?Swift 中的 Range 和 Stride 給出了答案,如果想要打印到 10 里面3的倍數(shù),可以使用 Range 通過(guò)使用語(yǔ)法 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
另外一種方式是通過(guò) stride 每隔三個(gè)元素訪問(wèn)一次。stride 可以用很多方法來(lái)創(chuàng)建,但是最常見(jiàn)的是 stride(from: , to:, by:),from value 就是跨步訪問(wèn)的起初值,然后 by 是每隔多少跨步值才能訪問(wèn)到 to 值。聽(tīng)起來(lái)有點(diǎn)繞,讓我們來(lái)看實(shí)際的代碼:

let byThrees = stride(from: 3, to: 10, by: 3)
for n in byThrees {
print(n)
}```
輸出結(jié)果為:3 6 9
從英語(yǔ)來(lái)看十分好讀,你也可以說(shuō)你從 3 數(shù)到 10 每隔3個(gè)數(shù)。這里我們創(chuàng)造 stride 并且用一個(gè)變量 byThrees 來(lái)存儲(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ù)組,非常適合訪問(wèn)或者過(guò)濾集合中某些元素的情況。這里回到我們的名稱集合的例子,如果想要前三個(gè)名稱,可以這樣寫(xiě):

let names = ["Robb", "Sansa", "Arya", "Jon"]
for nameIndex in names.indices {
if(nameIndex < 3) {
print(names[nameIndex])
}
}
Robb, Sansa, Arya````
在集合中還有枚舉的方法,它允許你通過(guò)遍歷下標(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 中還有很多方式來(lái)遍歷對(duì)象,但他們通常不是很常用。
也許你已經(jīng)發(fā)現(xiàn)我們的循環(huán)中同時(shí)給兩個(gè)變量賦值,index 和 name。它們被用逗號(hào)分隔并被括號(hào)括起來(lái),表示我們從 enumerated() 返回的兩個(gè)被命名的變量。
####函數(shù)和閉包
使用`func`來(lái)聲明函數(shù),使用名字和參數(shù)來(lái)調(diào)用函數(shù)。使用->來(lái)指定函數(shù)返回值的類型。 

//聲明creat方法,傳入personday兩個(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);

使用元組來(lái)讓一個(gè)函數(shù)返回多個(gè)值,元組的元素可以用名稱和數(shù)字來(lái)表示。

//聲明一個(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ù)可以訪問(wèn)外側(cè)函數(shù)的變量,你可以使用嵌套函數(shù)來(lái)重構(gòu)一個(gè)太長(zhǎng)或者太復(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)取的代碼。閉包中的代碼能訪問(wèn)閉包所建作用域中能得到的變 量和函數(shù),即使閉包是在一個(gè)不同的作用域被執(zhí)行的 - 你已經(jīng)在嵌套函數(shù)例子中所看到。你可以使用 {} 來(lái)創(chuàng)建 一個(gè)匿名閉包。使用` in `將參數(shù)和返回值類型聲明與閉包函數(shù)體進(jìn)行分離。

numbers.map({
(number: Int) -> Int in
let result = 3 * number
return result
})````

對(duì)象和類

使用 class 和類名來(lái)創(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)語(yǔ)法來(lái)訪問(wèn)實(shí)例的屬性和方法。

var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

這個(gè)版本的 Shape 類缺少了一些重要的東西:一個(gè)構(gòu)造函數(shù)來(lái)初始化類實(shí)例。使用 init 來(lái)創(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被用來(lái)區(qū)別實(shí)例變量。當(dāng)你創(chuàng)建實(shí)例的時(shí)候,像傳入函數(shù)參數(shù)一樣給類傳入構(gòu)造器的參數(shù)。每個(gè)屬性都 需要賦值——無(wú)論是通過(guò)聲明(就像 numberOfSides )還是通過(guò)構(gòu)造器(就像 name )。
如果你需要在刪除對(duì)象之前進(jìn)行一些清理工作,使用 deinit創(chuàng)建一個(gè)析構(gòu)函數(shù)。
子類的定義方法是在它們的類名后面加上父類的名字,用冒號(hào)分割。創(chuàng)建類的時(shí)候并不需要一個(gè)標(biāo)準(zhǔn)的根類,所以你可以忽略父類。
子類如果要重寫(xiě)父類的方法的話,需要用 override 標(biāo)記——如果沒(méi)有添加 override 就重寫(xiě)父類方法的話編譯器 會(huì)報(bào)錯(cuò)。編譯器同樣會(huì)檢測(cè) 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ǔ)存簡(jiǎn)單的屬性之外,屬性可以有 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í)行了三步:

  1. 設(shè)置子類聲明的屬性值
  2. 調(diào)用父類的構(gòu)造器
  3. 改變父類定義的屬性值。其他的工作比如調(diào)用方法getters 和 setters 也可以在這個(gè)階段完成。

如果你不需要計(jì)算屬性,但是仍然需要在設(shè)置一個(gè)新值之前或者之后運(yùn)行代碼,使用 willSet 和 didSet 。 比如,下面的類確保三角形的邊長(zhǎng)總是和正方形的邊長(zhǎng)相同。

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 來(lái)創(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 開(kāi)始每次加 1 的方式為原始值進(jìn)行賦值,不過(guò)你可以通過(guò)顯式賦值進(jìn)行改變
使用init?(rawValue:)初始化構(gòu)造器在原始值和枚舉值之間進(jìn)行轉(zhuǎn)換。

if let convertedRank = Rank(rawValue: 3) {
    let threeDescription = convertedRank.simpleDescription()
}

枚舉的成員值是實(shí)際值,并不是原始值的另一種表達(dá)方法。實(shí)際上,如果沒(méi)有比較有意義的原始值,你就不需要
提供原始值。

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 需要用全名來(lái)引用,因 為常量沒(méi)有顯式指定類型。在 switch 里,枚舉成員使用縮寫(xiě) .Hearts 來(lái)引用,因?yàn)?self 的值已經(jīng)知道是一個(gè) it 。已知變量類型的情況下你可以使用縮寫(xiě)。
一個(gè)枚舉成員的實(shí)例可以有實(shí)例值。相同枚舉成員的實(shí)例可以有不同的值。創(chuàng)建實(shí)例的時(shí)候傳入值即可。實(shí)例值和原始值是不同的:枚舉成員的原始值對(duì)于所有實(shí)例都是相同的,而且你是在定義枚舉的時(shí)候設(shè)置原始值。

使用 struct 來(lái)創(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 來(lái)聲明一個(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)鍵字用來(lái)標(biāo)記一個(gè)會(huì)修改結(jié)構(gòu)體的方法。 SimpleClass 的聲明不需要 標(biāo)記任何方法,因?yàn)轭愔械姆椒ㄍǔ?梢孕薷念悓傩?類的性質(zhì))。
使用 extension 來(lái)為現(xiàn)有的類型添加功能,比如新的方法和計(jì)算屬性。你可以使用擴(kuò)展在別處修改定義,甚至是 從外部庫(kù)或者框架引入的一個(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é)議的對(duì)象集合。當(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é)議的類型來(lái)表示錯(cuò)誤。

enum PrinterError: Error {
    case OutOfPaper
    case NoToner
    case OnFire 
}

使用throw來(lái)拋出一個(gè)錯(cuò)誤并使用 throws 來(lái)表示一個(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"
}

有多種方式可以用來(lái)進(jìn)行錯(cuò)誤處理。一種方式是使用 do-catch 。在 do 代碼塊中,使用 try 來(lái)標(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 塊來(lái)處理特定的錯(cuò)誤。參照 switch 中的 case 風(fēng)格來(lái)寫(xiě) 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 代碼塊來(lái)表示在函數(shù)返回前,函數(shù)中最后執(zhí)行的代碼。無(wú)論函數(shù)是否會(huì)拋出錯(cuò)誤,這段代碼都將執(zhí) 行。使用defer,可以把函數(shù)調(diào)用之初就要執(zhí)行的代碼和函數(shù)調(diào)用結(jié)束時(shí)的掃尾代碼寫(xiě)在一起,雖然這兩者的執(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)里寫(xiě)一個(gè)名字來(lái)創(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)庫(kù)中的可選類型 enum OptionalValue<Wrapped> {
case None
    case Some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .None
possibleInteger = .Some(100)

在類型名后面使用 where 來(lái)指定對(duì)類型的需求,比如,限定類型實(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à)的。

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

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

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