類型別名:
你可以使用?typealias?關(guān)鍵字來定義類型別名
當(dāng)你想要給現(xiàn)有類型起一個(gè)更有意義的名字時(shí),類型別名非常有用
typealias AudioSample = UInt16
var maxAmplitudeFound = AudioSample.min
AudioSample?被定義為?UInt16?的一個(gè)別名。因?yàn)樗莿e名,AudioSample.min?實(shí)際上是?UInt16.min,所以會(huì)給?maxAmplitudeFound?賦一個(gè)初值?0
元組:
元組(tuples)把多個(gè)值組合成一個(gè)復(fù)合值。元組內(nèi)的值可以是任意類型,并不要求是相同類型
(404, "Not Found")?是一個(gè)描述?HTTP 狀態(tài)碼(HTTP status code)的元組。HTTP 狀態(tài)碼是當(dāng)你請(qǐng)求網(wǎng)頁的時(shí)候 web 服務(wù)器返回的一個(gè)特殊值。如果你請(qǐng)求的網(wǎng)頁不存在就會(huì)返回一個(gè)?404 Not Found?狀態(tài)碼
let heep404Error = (404, "Not Found")
(404, "Not Found")?元組把一個(gè)?Int?值和一個(gè)?String?值組合起來表示 HTTP 狀態(tài)碼的兩個(gè)部分:一個(gè)數(shù)字和一個(gè)人類可讀的描述。這個(gè)元組可以被描述為“一個(gè)類型為?(Int, String)?的元組
你可以將一個(gè)元組的內(nèi)容分解(decompose)成單獨(dú)的常量和變量
let (statusCode, statusMessage) = http404Error
print("the statusCode is \(statusCode)")
print("the statusMessage is \(statusMessage)")
如果你只需要一部分元組值,分解的時(shí)候可以把要忽略的部分用下劃線(_)標(biāo)記
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
此外,你還可以通過下標(biāo)來訪問元組中的單個(gè)元素,下標(biāo)從零開始
print("The status code is \(http404Error.0)")// 輸出“The status code is 404”print("The status message is \(http404Error.1)")// 輸出“The status message is Not Found”
你可以在定義元組的時(shí)候給單個(gè)元素命名
let http200Status = (statusCode: 200,description: "OK")
給元組中的元素命名后,你可以通過名字來獲取這些元素的值
print("The status code is \(http200Status.statusCode)")// 輸出“The status code is 200”print("The status message is \(http200Status.description)")// 輸出“The status message is OK”
可選類型:
使用可選類型(optionals)來處理值可能缺失的情況??蛇x類型表示兩種可能:
或者有值, 你可以解析可選類型訪問這個(gè)值, 或者根本沒有值
下面的例子使用這種構(gòu)造器來嘗試將一個(gè)?String?轉(zhuǎn)換成?Int
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
因?yàn)樵摌?gòu)造器可能會(huì)失敗,所以它返回一個(gè)可選類型(optional)Int,而不是一個(gè)?Int。一個(gè)可選的?Int?被寫作?Int??而不是?Int。問號(hào)暗示包含的值是可選類型,也就是說可能包含?Int?值也可能不包含值
你可以給可選變量賦值為?nil?來表示它沒有值
var serverResponseCode: Int? = 404
serverResponseCode = nil
nil?不能用于非可選的常量和變量。如果你的代碼中有常量或者變量需要處理值缺失的情況,請(qǐng)把它們聲明成對(duì)應(yīng)的可選類型
如果你聲明一個(gè)可選常量或者變量但是沒有賦值,它們會(huì)自動(dòng)被設(shè)置為?nil
var surveyAnswer: String?
Swift 的?nil?和 Objective-C 中的?nil?并不一樣。在 Objective-C 中,nil?是一個(gè)指向不存在對(duì)象的指針。在 Swift 中,nil?不是指針——它是一個(gè)確定的值,用來表示值缺失。任何類型的可選狀態(tài)都可以被設(shè)置為?nil,不只是對(duì)象類型
如果可選類型有值,它將不等于?nil
if convertedNumber != nil {
? ? print("convertedNumber contains some integer value.")
}// 輸出“convertedNumber contains some integer value.”
當(dāng)你確定可選類型確實(shí)包含值之后,你可以在可選的名字后面加一個(gè)感嘆號(hào)(!)來獲取值。這個(gè)驚嘆號(hào)表示“我知道這個(gè)可選有值,請(qǐng)使用它?!边@被稱為可選值的強(qiáng)制解析
if convertedNumber != nil {
? ? print("convertedNumber has an integer value of \(convertedNumber!).")
}// 輸出“convertedNumber has an integer value of 123.”
使用?!?來獲取一個(gè)不存在的可選值會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。使用?!?來強(qiáng)制解析值之前,一定要確定可選包含一個(gè)非?nil?的值
可選綁定:
使用可選綁定(optional binding)來判斷可選類型是否包含值,如果包含就把值賦給一個(gè)臨時(shí)常量或者變量??蛇x綁定可以用在?if?和?while?語句中,這條語句不僅可以用來判斷可選類型中是否有值,同時(shí)可以將可選類型中的值賦給一個(gè)常量或者變量
if let actualNumber = Int(possibleNumber) {
? ? print("\'\(possibleNumber)\' has an integer value of \(actualNumber)")
} else {
? ? print("\'\(possibleNumber)\' could not be converted to an integer")
}// 輸出“'123' has an integer value of 123”
這段代碼可以被理解為:
“如果?Int(possibleNumber)?返回的可選?Int?包含一個(gè)值,創(chuàng)建一個(gè)叫做?actualNumber?的新常量并將可選包含的值賦給它?!?/p>
如果轉(zhuǎn)換成功,actualNumber?常量可以在?if?語句的第一個(gè)分支中使用。它已經(jīng)被可選類型?包含的?值初始化過,所以不需要再使用?!?后綴來獲取它的值。在這個(gè)例子中,actualNumber?只被用來輸出轉(zhuǎn)換結(jié)果
你可以包含多個(gè)可選綁定或多個(gè)布爾條件在一個(gè)?if?語句中,只要使用逗號(hào)分開就行。只要有任意一個(gè)可選綁定的值為?nil,或者任意一個(gè)布爾條件為?false,則整個(gè)?if?條件判斷為?false,這時(shí)你就需要使用嵌套?if?條件語句來處理
if let firstNumber = Int("4"), let secondNumber = Int("42"),?firstNumber <?secondNumber &&?secondNumber <?100{?
print("\(firstNumber) < \(secondNumber) < 100")
}
if let firstNumber = Int("4") {
? ? if let secondNumber = Int("42") {
? ? ? ? if firstNumber < secondNumber && secondNumber < 100 {
? ? ? ? ? ? print("\(firstNumber) < \(secondNumber) < 100")
? ? ? ? }
? ? }
}
在?if?條件語句中使用常量和變量來創(chuàng)建一個(gè)可選綁定,僅在?if?語句的句中(body)中才能獲取到值。相反,在?guard?語句中使用常量和變量來創(chuàng)建一個(gè)可選綁定,僅在?guard?語句外且在語句后才能獲取到值
隱式解析可選類型:
如上所述,可選類型暗示了常量或者變量可以“沒有值”??蛇x可以通過?if?語句來判斷是否有值,如果有值的話可以通過可選綁定來解析值。
有時(shí)候在程序架構(gòu)中,第一次被賦值之后,可以確定一個(gè)可選類型總會(huì)有值。在這種情況下,每次都要判斷和解析可選值是非常低效的,因?yàn)榭梢源_定它總會(huì)有值
這種類型的可選狀態(tài)被定義為隱式解析可選類型(implicitly unwrapped optionals)。把想要用作可選的類型的后面的問號(hào)(String?)改成感嘆號(hào)(String!)來聲明一個(gè)隱式解析可選類型
當(dāng)可選類型被第一次賦值之后就可以確定之后一直有值的時(shí)候,隱式解析可選類型非常有用。隱式解析可選類型主要被用在 Swift 中類的構(gòu)造過程中
一個(gè)隱式解析可選類型其實(shí)就是一個(gè)普通的可選類型,但是可以被當(dāng)做非可選類型來使用,并不需要每次都使用解析來獲取可選值。下面的例子展示了可選類型?String?和隱式解析可選類型?String?之間的區(qū)別
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要感嘆號(hào)來獲取值
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要感嘆號(hào)
你可以把隱式解析可選類型當(dāng)做一個(gè)可以自動(dòng)解析的可選類型。你要做的只是聲明的時(shí)候把感嘆號(hào)放到類型的結(jié)尾,而不是每次取值的可選名字的結(jié)尾
如果你在隱式解析可選類型沒有值的時(shí)候嘗試取值,會(huì)觸發(fā)運(yùn)行時(shí)錯(cuò)誤。和你在沒有值的普通可選類型后面加一個(gè)驚嘆號(hào)一樣
你仍然可以把隱式解析可選類型當(dāng)做普通可選類型來判斷它是否包含值
if assumedString != nil {
? ? print(assumedString!)
}// 輸出“An implicitly unwrapped optional string.”
你也可以在可選綁定中使用隱式解析可選類型來檢查并解析它的值
if let definiteString = assumedString {
? ? print(definiteString)
}// 輸出“An implicitly unwrapped optional string.”
如果一個(gè)變量之后可能變成?nil?的話請(qǐng)不要使用隱式解析可選類型。如果你需要在變量的生命周期中判斷是否是?nil?的話,請(qǐng)使用普通可選類型
錯(cuò)誤處理:
你可以使用?錯(cuò)誤處理(error handling)?來應(yīng)對(duì)程序執(zhí)行中可能會(huì)遇到的錯(cuò)誤條件。相對(duì)于可選中運(yùn)用值的存在與缺失來表達(dá)函數(shù)的成功與失敗,錯(cuò)誤處理可以推斷失敗的原因,并傳播至程序的其他部分
func canThrowAnError() throws {
? ? // 這個(gè)函數(shù)有可能拋出錯(cuò)誤
}
一個(gè)函數(shù)可以通過在聲明中添加?throws?關(guān)鍵詞來拋出錯(cuò)誤消息。當(dāng)你的函數(shù)能拋出錯(cuò)誤消息時(shí),你應(yīng)該在表達(dá)式中前置?try?關(guān)鍵詞
do {
try?canThrowAnError()// 沒有錯(cuò)誤消息拋出
} catch {
//有一個(gè)錯(cuò)誤消息拋出}
一個(gè)?do?語句創(chuàng)建了一個(gè)新的包含作用域,使得錯(cuò)誤能被傳播到一個(gè)或多個(gè)?catch?從句。
這里有一個(gè)錯(cuò)誤處理如何用來應(yīng)對(duì)不同錯(cuò)誤條件的例子
funcmakeASandwich() throws {
? ? // ...}do {
? ? try makeASandwich()
? ? eatASandwich()
} catch SandwichError.outOfCleanDishes {
? ? washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
? ? buyGroceries(ingredients)
}
在此例中,makeASandwich()(做一個(gè)三明治)函數(shù)會(huì)拋出一個(gè)錯(cuò)誤消息如果沒有干凈的盤子或者某個(gè)原料缺失。因?yàn)?makeASandwich()?拋出錯(cuò)誤,函數(shù)調(diào)用被包裹在?try?表達(dá)式中。將函數(shù)包裹在一個(gè)?do?語句中,任何被拋出的錯(cuò)誤會(huì)被傳播到提供的?catch?從句中。
如果沒有錯(cuò)誤被拋出,eatASandwich()?函數(shù)會(huì)被調(diào)用。如果一個(gè)匹配?SandwichError.outOfCleanDishes?的錯(cuò)誤被拋出,washDishes()?函數(shù)會(huì)被調(diào)用。如果一個(gè)匹配?SandwichError.missingIngredients?的錯(cuò)誤被拋出,buyGroceries(_:)?函數(shù)會(huì)被調(diào)用,并且使用?catch?所捕捉到的關(guān)聯(lián)值?[String]?作為參數(shù)
斷言和先決條件:
斷言和先決條件是在運(yùn)行時(shí)所做的檢查。你可以用他們來檢查在執(zhí)行后續(xù)代碼之前是否一個(gè)必要的條件已經(jīng)被滿足了。如果斷言或者先決條件中的布爾條件評(píng)估的結(jié)果為 true(真),則代碼像往常一樣繼續(xù)執(zhí)行。如果布爾條件評(píng)估結(jié)果為 false(假),程序的當(dāng)前狀態(tài)是無效的,則代碼執(zhí)行結(jié)束,應(yīng)用程序中止
你使用斷言和先決條件來表達(dá)你所做的假設(shè)和你在編碼時(shí)候的期望。你可以將這些包含在你的代碼中。斷言幫助你在開發(fā)階段找到錯(cuò)誤和不正確的假設(shè),先決條件幫助你在生產(chǎn)環(huán)境中探測(cè)到存在的問題
使用斷言和先決條件不是一個(gè)能夠避免出現(xiàn)程序出現(xiàn)無效狀態(tài)的編碼方法。然而,如果一個(gè)無效狀態(tài)程序產(chǎn)生了,斷言和先決條件可以強(qiáng)制檢查你的數(shù)據(jù)和程序狀態(tài),使得你的程序可預(yù)測(cè)的中止(譯者:不是系統(tǒng)強(qiáng)制的,被動(dòng)的中止),并幫助使這個(gè)問題更容易調(diào)試。一旦探測(cè)到無效的狀態(tài),執(zhí)行則被中止,防止無效的狀態(tài)導(dǎo)致的進(jìn)一步對(duì)于系統(tǒng)的傷害
斷言和先決條件的不同點(diǎn)是,他們什么時(shí)候進(jìn)行狀態(tài)檢測(cè):斷言僅在調(diào)試環(huán)境運(yùn)行,而先決條件則在調(diào)試環(huán)境和生產(chǎn)環(huán)境中運(yùn)行。在生產(chǎn)環(huán)境中,斷言的條件將不會(huì)進(jìn)行評(píng)估。這個(gè)意味著你可以使用很多斷言在你的開發(fā)階段,但是這些斷言在生產(chǎn)環(huán)境中不會(huì)產(chǎn)生任何影響
使用斷言進(jìn)行調(diào)試:
你可以調(diào)用 Swift 標(biāo)準(zhǔn)庫的?assert(_:_:file:line:)?函數(shù)來寫一個(gè)斷言。向這個(gè)函數(shù)傳入一個(gè)結(jié)果為?true?或者?false?的表達(dá)式以及一條信息,當(dāng)表達(dá)式的結(jié)果為?false?的時(shí)候這條信息會(huì)被顯示
let age = -3
assert(age >= 0, "A person's age cannot be less than zero")// 因?yàn)?age < 0,所以斷言會(huì)觸發(fā)
在這個(gè)例子中,只有?age >= 0?為?true?時(shí),即?age?的值非負(fù)的時(shí)候,代碼才會(huì)繼續(xù)執(zhí)行。如果?age?的值是負(fù)數(shù),就像代碼中那樣,age >= 0?為?false,斷言被觸發(fā),終止應(yīng)用
如果代碼已經(jīng)檢查了條件,你可以使用?assertionFailure(_:file:line:)?函數(shù)來表明斷言失敗了,例如:
if age > 10 {
? ? print("You can ride the roller-coaster or the ferris wheel.")
} else if age > 0 {
? ? print("You can ride the ferris wheel.")
} else {
? ? assertionFailure("A person's age can't be less than zero.")
}
強(qiáng)制執(zhí)行先決條件:
當(dāng)一個(gè)條件可能為假,但是繼續(xù)執(zhí)行代碼要求條件必須為真的時(shí)候,需要使用先決條件。例如使用先決條件來檢查是否下標(biāo)越界,或者來檢查是否將一個(gè)正確的參數(shù)傳給函數(shù)。
你可以使用全局?precondition(_:_:file:line:)?函數(shù)來寫一個(gè)先決條件。向這個(gè)函數(shù)傳入一個(gè)結(jié)果為?true?或者?false?的表達(dá)式以及一條信息,當(dāng)表達(dá)式的結(jié)果為?false?的時(shí)候這條信息會(huì)被顯示
// 在一個(gè)下標(biāo)的實(shí)現(xiàn)里...precondition(index >0,"Index must be greater than zero.")
你可以調(diào)用preconditionFailure(_:file:line:)?方法來表明出現(xiàn)了一個(gè)錯(cuò)誤,例如,switch 進(jìn)入了 default 分支,但是所有的有效值應(yīng)該被任意一個(gè)其他分支(非 default 分支)處理
賦值運(yùn)算符:
與 C 語言和 Objective-C 不同,Swift 的賦值操作并不返回任何值。所以下面語句是無效的
let x = 5, y = 5;
if x = y{
//此句錯(cuò)誤, 因?yàn)閤=y并不返回任何值}
通過將?if x = y?標(biāo)記為無效語句,Swift 能幫你避免把 (==)錯(cuò)寫成(=)這類錯(cuò)誤的出現(xiàn)
求余運(yùn)算符:
在對(duì)負(fù)數(shù)?b?求余時(shí),b?的符號(hào)會(huì)被忽略。這意味著?a % b?和?a % -b?的結(jié)果是相同的
let c = 9 % 4
let d = 9 % -4結(jié)果是相同的, 余數(shù)都是1
比較運(yùn)算符:
Swift 也提供恒等(===)和不恒等(!==)這兩個(gè)比較符來判斷兩個(gè)對(duì)象是否引用同一個(gè)對(duì)象實(shí)例
(1, "zebra") < (2, "apple")? // true,因?yàn)?1 小于 2
(3, "apple") < (3, "bird")? ? // true,因?yàn)?3 等于 3,但是 apple 小于 bird
(4, "dog") == (4, "dog")? ? // true,因?yàn)?4 等于 4,dog 等于 dog
("blue", -1) < ("purple", 1)? ? ? // 正常,比較的結(jié)果為 true
//("blue", false) < ("purple", true) // 錯(cuò)誤,因?yàn)?< 不能比較布爾類型
Swift 標(biāo)準(zhǔn)庫只能比較七個(gè)以內(nèi)元素的元組比較函數(shù)。如果你的元組元素超過七個(gè)時(shí),你需要自己實(shí)現(xiàn)比較運(yùn)算符
空合運(yùn)算符:
空合運(yùn)算符(a ?? b)將對(duì)可選類型?a?進(jìn)行空判斷,如果?a?包含一個(gè)值就進(jìn)行解包,否則就返回一個(gè)默認(rèn)值?b。表達(dá)式?a?必須是 Optional 類型。默認(rèn)值?b?的類型必須要和?a?存儲(chǔ)值的類型保持一致
a != nil ? a! : b
上述代碼使用了三元運(yùn)算符。當(dāng)可選類型?a?的值不為空時(shí),進(jìn)行強(qiáng)制解封(a!),訪問?a?中的值;反之返回默認(rèn)值?b。無疑空合運(yùn)算符(??)提供了一種更為優(yōu)雅的方式去封裝條件判斷和解封兩種行為,顯得簡潔以及更具可讀性
let defaultColorName = "red"
var userDefinedColorName: String? //默認(rèn)值為 nil
var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 的值為空,所以 colorNameToUse 的值為 "red"
userDefinedColorName?變量被定義為一個(gè)可選的?String?類型,默認(rèn)值為?nil。由于?userDefinedColorName?是一個(gè)可選類型,我們可以使用空合運(yùn)算符去判斷其值。在上一個(gè)例子中,通過空合運(yùn)算符為一個(gè)名為?colorNameToUse?的變量賦予一個(gè)字符串類型初始值。
由于?userDefinedColorName?值為空,因此表達(dá)式?userDefinedColorName ?? defaultColorName?返回?defaultColorName?的值,即?red。
如果你分配一個(gè)非空值(non-nil)給?userDefinedColorName,再次執(zhí)行空合運(yùn)算,運(yùn)算結(jié)果為封包在?userDefaultColorName?中的值,而非默認(rèn)值。
userDefinedColorName ="green"
colorNameToUse = userDefinedColorName ?? defaultColorName// userDefinedColorName 非空,因此 colorNameToUse 的值為 "green"
多行字符串字面量:
如果你需要一個(gè)字符串是跨越多行的,那就使用多行字符串字面量 — 由一對(duì)三個(gè)雙引號(hào)包裹著的具有固定順序的文本字符集
let quotation = """
The White Rabbit put on his spectacles.? "Where shall I begin,
please your Majesty?" he asked.
"Begin at the beginning," the King said gravely, "and go on
till you come to the end; then stop."
"""
一個(gè)多行字符串字面量包含了所有的在開啟和關(guān)閉引號(hào)(""")中的行。這個(gè)字符從開啟引號(hào)(""")之后的第一行開始,到關(guān)閉引號(hào)(""")之前為止, ?
并且引號(hào)需要單獨(dú)占兩行
這就意味著字符串開啟引號(hào)之后(""")或者結(jié)束引號(hào)(""")之前都沒有換行符號(hào)
let singleLineString = "These are the same."
let multilineString = """
These are the same.
"""
一個(gè)多行字符串字面量能夠縮進(jìn)來匹配周圍的代碼。關(guān)閉引號(hào)(""")之前的空白字符串告訴 Swift 編譯器其他各行多少空白字符串需要忽略。然而,如果你在某行的前面寫的空白字符串超出了關(guān)閉引號(hào)(""")之前的空白字符串,則超出部分將被包含在多行字符串字面量中。

字符串是值類型:
在 Swift 中?String?類型是值類型。如果你創(chuàng)建了一個(gè)新的字符串,那么當(dāng)其進(jìn)行常量、變量賦值操作,或在函數(shù)/方法中傳遞時(shí),會(huì)進(jìn)行值拷貝。在前述任一情況下,都會(huì)對(duì)已有字符串值創(chuàng)建新副本,并對(duì)該新副本而非原始字符串進(jìn)行傳遞或賦值操作
在實(shí)際編譯時(shí),Swift 編譯器會(huì)優(yōu)化字符串的使用,使實(shí)際的復(fù)制只發(fā)生在絕對(duì)必要的情況下,這意味著你將字符串作為值類型的同時(shí)可以獲得極高的性能
字符串可以通過傳遞一個(gè)值類型為?Character?的數(shù)組作為自變量來初始化:
let catCharacters: [Character] = ["C","a","t","!","??"]
let catString =String(catCharacters)
print(catString)// 打印輸出:“Cat!??”
連接字符串和字符
字符串可以通過加法運(yùn)算符(+)相加在一起(或稱“連接”)創(chuàng)建一個(gè)新的字符串
let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2// welcome 現(xiàn)在等于 "hello there"
你也可以通過加法賦值運(yùn)算符(+=)將一個(gè)字符串添加到一個(gè)已經(jīng)存在字符串變量上:
var instruction ="look over"?
instruction += string2// instruction 現(xiàn)在等于 "look over there"
你可以用?append()?方法將一個(gè)字符附加到一個(gè)字符串變量的尾部:
let exclamationMark:Character="!"
welcome.append(exclamationMark)// welcome 現(xiàn)在等于 "hello there!"
你不能將一個(gè)字符串或者字符添加到一個(gè)已經(jīng)存在的字符變量上,因?yàn)樽址兞恐荒馨粋€(gè)字符。
let badStart = """
one
two
"""
let end = """
three
"""
print(badStart + end)// 打印兩行:// one// twothree
let goodStart = """
one
two
"""
print(goodStart + end)// 打印三行:// one// two// three
字符串插值
字符串插值是一種構(gòu)建新字符串的方式,可以在其中包含常量、變量、字面量和表達(dá)式。字符串字面量和多行字符串字面量都可以使用字符串插值。你插入的字符串字面量的每一項(xiàng)都在以反斜線為前綴的圓括號(hào)中
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"http:// message 是 "3 times 2.5 is 7.5"
插值字符串中寫在括號(hào)中的表達(dá)式不能包含非轉(zhuǎn)義反斜杠(\),并且不能包含回車或換行符。不過,插值字符串可以包含其他字面量。
比較字符串
字符串/字符相等
字符串/字符可以用等于操作符(==)和不等于操作符(!=)