關于 Swift
重要這個文檔所包含的準備信息, 是關于開發的 API 和技術的。這個信息可能會改變, 根據這個文檔開發的軟件, 應該使用最新的操作系統軟件進行測試。
Swift 開發軟件是極其出色的, 不管是為了手機, 桌面, 服務器還是其他什么。它是一門安全, 快速和交互式的編程語言。編譯器和語言都經過了優化。
Swift 對新手開發者很友好。它是一門工業質量級別的編程語言, 和腳本語言一樣富有表現力和令人愉悅。在 playground 中寫 Swift 代碼讓你所見即所得, 完全不需要編譯和運行程序。
Swift 采用了現代編程模式, 定義了大量的常見編程錯誤:
變量在使用之前總是需要初始化。
數組所以會判斷越界錯誤。
整數會判斷是否溢出。
可選類型確??罩当伙@式處理。
內存會自動管理。
錯誤處理允許從失敗恢復。
Swift 代碼被編譯和優化以充分使用現代硬件。集合安全性和速度使得 Swift 成為一個優秀的選擇, 可以編寫從 “Hello, world!” 到整個操作系統。
Swift 使用現代、輕量級的語法, 集合了強有力的類型推斷和模式匹配, 讓復雜的想法可以用一個清晰和簡單的方式來實現。這樣以來, 代碼不僅很容易編寫, 也容易讀取和維護。
Swift 已經面世多年, 它會繼續發展新的特性和能力。我們的目標是星辰大海。我們等不及想看看你能創造什么出來了。
版本兼容性
這個教程描述 Swift 4.0, 它是包含在 Xcode 9 中默認的 Swift 版本。你可以使用 Xcode 9 編譯使用了 Swift 4 或者 Swift 3 編寫的代碼。
備注當 Swift 4 編譯器使用 Swift 3 代碼時, 它標記語言的版本是 3.2—這就意味著你可以使用條件編譯塊比如like #if swift(>=3.2)來編寫代碼, 以兼容多個版本的 Swift 編譯器。
當你使用 Xcode 9 去編譯 Swift 3 代碼時, 多數 Swift 4 的功能是不可用的。就是說, 下面的特性只能在 Swift 4 代碼中使用:
子字符串操作會返回子字符串的類型, 而不是。
@objc 屬性隱式添加到更少的地方。
在相同的文件里擴展一個類型, 可以訪問這個類型的私有成員。
如果你有一個大項目, 分成多個框架, 你可以把你的代碼從 Swift 3 移植到 Swift 4, 每次一個框架。
基本介紹
Swift 是為 iOS,macOS,watchOS 和 tvOS 應用開發的一門新的開發語言,盡管如此,Swift 很多部分都跟你以往從事 C 和Objective-C 開發經驗很相似。
基于 C 和 Objective-C 的數據類型,Swift 提供了自己的版本,包括 Int, Double,Float,Bool 和 String。它同時也提供了三種集合類型,Array,Set 和 Dictionary。
跟C類似,Swift 通過變量名來存取值。Swift 還大量使用常量,這里的常量比C的常量更加強大。常量使用貫穿 Swift,用來讓代碼更加安全和容易推斷,特別是在你不想變量值發生改變的時候。
除了基本類型,Swift 還引進了 Objective-C 中沒有的高級類型,比如元組。元組讓你可以創建和傳遞多值。你可以在一個函數里返回元組來作為一個單獨的混合值。
Swift 還引進了可選類型,用來處理缺值的情況??蛇x的意思是‘這里有一個值,它等于x’ 或者‘這里沒有任何值’。用可選值跟Objective-C 里使用 nil 指針有點像,不過可選類型可以服務任何類型,不僅僅是類類型??蛇x類型不僅僅安全而且更容易表達意思,它是 Swift 最重要特性里的核心特性。
Swift 是一門類型安全的語言,這也就意味著這門語言可以讓你更清晰的知道代碼使用的值類型。如果你的代碼需要一個 String,類型安全會保護你不至于傳遞一個 Int 類型。同樣,類型安全也會保護你,讓你不會把非可選類型傳遞給需要可選類型的代碼塊。類型安全讓你在開發階段盡快發現問題,修復錯誤。
Swift 是為 iOS,macOS,watchOS 和 tvOS 應用開發的一門新的開發語言,盡管如此,Swift 很多部分都跟你以往從事 C 和Objective-C 開發經驗很相似。
基于 C 和 Objective-C 的數據類型,Swift 提供了自己的版本,包括 Int, Double,Float,Bool 和 String。它同時也提供了三種集合類型,Array,Set 和 Dictionary。
跟C類似,Swift 通過變量名來存取值。Swift 還大量使用常量,這里的常量比C的常量更加強大。常量使用貫穿 Swift,用來讓代碼更加安全和容易推斷,特別是在你不想變量值發生改變的時候。
除了基本類型,Swift 還引進了 Objective-C 中沒有的高級類型,比如元組。元組讓你可以創建和傳遞多值。你可以在一個函數里返回元組來作為一個單獨的混合值。
Swift 還引進了可選類型,用來處理缺值的情況??蛇x的意思是‘這里有一個值,它等于x’ 或者‘這里沒有任何值’。用可選值跟Objective-C 里使用 nil 指針有點像,不過可選類型可以服務任何類型,不僅僅是類類型。可選類型不僅僅安全而且更容易表達意思,它是 Swift 最重要特性里的核心特性。
Swift 是一門類型安全的語言,這也就意味著這門語言可以讓你更清晰的知道代碼使用的值類型。如果你的代碼需要一個 String,類型安全會保護你不至于傳遞一個 Int 類型。同樣,類型安全也會保護你,讓你不會把非可選類型傳遞給需要可選類型的代碼塊。類型安全讓你在開發階段盡快發現問題,修復錯誤。
1.常量和變量
常量和變量是一個有特定類型的值,帶有一個名字。常量值一旦確定就不能改變,變量在可用隨時改變其值。
2.定義常量和變量
常量和變量必須要在使用前定義,常量使用let關鍵字,變量使用var關鍵字。下面是一個例子來展示如何使用常量和變量,這個例子是跟蹤用戶嘗試的登錄次數。
let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
這段代碼可以這樣解讀:
定義個常量 maximumNumberOfLoginAttempts ,給它一個值10.然后定義一個變量 currentLoginAttempt,給它一個值0.
在這個例子里,最大登錄次數定義為一個常量,因為最大登錄數不會改變。當前登錄數定義為變量,因為這個值隨著用戶的登錄嘗試會逐漸增長。
你也可以定義多個常量或變量在一行,用逗號分開即可:
var x = 0.0, y = 0.0, z = 0.0
3.類型注釋
當定義常量或者變量的時候,你可以提供一個類型注釋,這樣可以更清楚的知道存儲類型是什么。在變量名或者常量名后面加一個冒號,然后一個空格,然后是要使用的類型。
這個例子為變量 welcomeMessage 提供一個類型解釋,來說明這個變量可以存儲 String 值:
var welcomeMessage: String
這段代碼可以理解為:
定義個變量叫 welcomeMessage ,它的類型是 String.
welcomeMessage 變量現在可以存儲任何的字符串:
welcomeMessage = "Hello"
你可以在一行定義多個相關的相同類型的變量,用逗號分開,然后再最后加上類型注釋:
var red, green, blue: Double
4.常量和變量命名
常量和變量命名可以包含幾乎任何的字符,包括Unicode字符:
let π = 3.14159
let 你好 = "你好世界"
let ???? = "dogcow"
常量和變量命名不能包含空格字符,數學符號,箭頭,私有(無效)的 Unicode 字符碼,或者-等。也不能以數字開始,雖然數字可以出現在名字里的任何地方。
一旦你給常量或者變量確定了類型,你就不能用同樣的名字來重定義它們,或者改變他們存儲的值類型。你也不能把常量改為變量,或者把變量變成常量。
你可以把常量的值改變成同樣類型的其他的值。這個例子里,變量 friendlyWelcome 的值從”Hello!” 變成 “Bonjour!”:
var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcome is now "Bonjour!"
與變量不同,常量值一旦確定就不能再改變。如果嘗試改變編譯器會報錯:
let languageName = "Swift"
languageName = "Swift++"
// 這是一個編譯期錯誤: languageName 不能修改。
5.打印常量和變量
你可以使用 print(_:separator:terminator:) 函數打印常量或者變量的當前值:
print(friendlyWelcome)
// 打印 "Bonjour!"
print(:separator:terminator:) 是一個全局函數,可以打印一個或者多個值。在 Xcode 里,例如,print(:separator:terminator:) 打印結果會出現在控制臺里。separator 和 terminator 參數都有默認值, 所以你可以忽略他們。 默認的, 這個函數打印完會加上換行符。 如果不想打印后換行, 傳入一個空字符串作為終止—例如, print(someValue, terminator: “”)
Swift 使用字符串插入在長字符串里插入常量或者變量名, 同時會提示 Swift 去用當前的常量值活變量值來替換它。 用括號包含名字,然后在前面加上反斜杠:
print("The current value of friendlyWelcome is \(friendlyWelcome)")
// 打印 "The current value of friendlyWelcome is Bonjour!"
6.注釋
在代碼中,把注釋加到不執行的文本,作為一個備注或者提醒。代碼編譯的時候,注釋會被編譯器忽略。
Swift 注釋和 C 語言注釋很像,不再贅述。
7.分號
跟其他語言不同,Swift 不要求在每條語句后寫分號(;),不過,在一行寫很多語句的時候,還是需要帶上分號的:
let cat = "??"; print(cat)
// 打印 "??"
8.整數
Integers 意思是整個數字沒有小數,比如 42 和 -23,整數要么是 signed(負數,0,正數) 要么是 unsigned(正數或者0)
Swift 提供8,16,32 和 64位的有符號和無符號的整數。這些整數類型和C語言很像,8位的無符號的整數是 UInt8,32位的有符號正數是Int32.
9.整數邊界
你可以用 min 和 max 屬性來獲取每個整數類型的最小值和最大值:
let minValue = UInt8.min// minValue 等于 0, 類型是 UInt8
let maxValue = UInt8.max// maxValue 等于 255, 類型是 UInt8
10.Int、UInt、浮點數
int:大多數情況下,你不需要指定整數的位數。Swift 提供了額外的整數類型 Int ,這個和當前平臺的本地字數一樣:
32位機器,Int 等于 Int32
64位機器,Int 等于 Int64
除非你要用規定大小的整數,否則,一般只要用 Int 就可以了。
UInt:Swift 也提供了無符號的整形,UInt,這個和當前平臺的本地字數也是一樣的:
32位機器,UInt 等于 UInt32
64位機器,UInt 等于 UInt64
浮點數:是帶有小數部分的數字,比如 3.14159,0.1 和 -273.15.
浮點數類型比整數類型表達更多的值域,比存在Int中的值更大或者更小。Swift 提供了兩個有符號的浮點數類型:
Double 表示64位的浮點數。
Float 表示32位的浮點數。
11.類型安全與推斷
Swift 是類型安全的語音。類型安全的語言鼓勵你清楚知道自己代碼使用的值的類型。如果你的代碼需要 String 類型,那么你就不要傳遞Int 給它。
因為 Swift 是類型安全的,在編譯代碼的時候類型檢查會執行來標記不匹配的類型錯誤。這讓你可以盡可能早的發現代碼中的錯誤。
類型檢查幫你避免使用不同類型的時候犯錯,然而,這并不意味你必須給所有的常量和變量指定類型。你不需要指定類型,Swift 使用類型推斷來推斷合適的類型。類型推斷使得編譯器可以在編譯代碼的時候自動推斷表達式的類型,只是通過簡單的檢查你提供的值。
因為有類型推斷,Swift 對類型聲明要求的比其他語言要少的多。常量和變量依然要顯示輸入,但是大部分指定類型的工作都已經幫你做了。
類型推斷在給常量或者變量賦初值的時候尤為有用。這個發生在你聲明常量或者變量,并給他們指定(字面量)的時候。所謂的字面量就是指直接出現在你的源碼中的值,比如下面例子里的 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 類型
12.數值字面量
整數字面量這可以寫:
十進制數字,沒有前綴
二進制數字,用0b做前綴
八進制數字,用0o做前綴
十六進制數字,用0x做前綴
下面所有整數字面量值都是17:
let decimalInteger = 17
let binaryInteger = 0b10001? ? ? // 17 in binary notation
let octalInteger = 0o21? ? ? ? ? // 17 in octal notation
let hexadecimalInteger = 0x11? ? // 17 in hexadecimal notation
浮點數字面量可以是十進制或者十六進制。在十進制點的兩邊都必須有數字。十進制浮點數也有個可選的指數,用大小寫 e 標明;十六進制浮點數也有指數,用大小寫的 p 標明。
用 exp 指數表示十進制數, 基礎數字乘以 10exp:
1.25e2 意思是 1.25 x 102, 或者是 125.0.
1.25e-2 意思是 1.25 x 10-2, 或者是 0.0125.
用 exp 指數表示十六進制數, 基礎數字乘以 2exp:
0xFp2 意思是 15 x 22, 或者是 60.0.
0xFp-2 意思是 15 x 2-2, 或者是 3.75.
下面所有這些浮點數字面量十進制數都是 12.1875:
let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0
13.數值類型轉換
在你的代碼中使用 Int 做為一般用途的整形常量和變量,盡快知道他們是非負數的。日常開發使用默認整形類型意味著整形變量和常量立即可以使用,而且它們匹配整形字面量的推斷類型。
特別指定的工作才會使用其他的整形類型,因為指定大小的整形類型需要額外的開銷。日常開發中,使用指定大小的類型幫助捕獲特定值的溢出,同時記錄被使用的數據。
14.整型轉換
不同數值類型,存儲在常量或變量中的數值范圍是不同的。一個 Int8 常量或者變量可以存儲 -128 到 127,UInt8 常量或者變量能存儲 0 到 255 之間的數值。如果數值不適合指定大小的整形類型,編譯后會報錯。
let cannotBeNegative: UInt8 = -1
// UInt8 cannot store negative numbers, and so this will report an error
let tooBig: Int8 = Int8.max + 1
// Int8 無法存儲超過它能存儲的最大值,
// 因此會報錯
為了轉換一個特定數值類型,先用一個存在的值初始化一個新的想要類型的新數值。下面的例子,常量 twoThousand 的類型是UInt16, 而常量 one 的類型是 UInt8 .他們不能直接相加,因為類型不同。取而代之的是,這個例子調用 UInt16(one)來創建一個新的UInt16,并用 one 初始化它,并在原來的位置使用這個值:
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
因為加好兩邊的值類型都是 UInt16 ,所以加法是進行的。輸出常量推斷是 UInt16 ,因為它是兩個 UInt16 值的和。
SomeType(ofInitialValue) 默認調用 Swift 類型的初始化函數,然后傳遞一個初始值。這個語句之前, UInt16 有一個構造器接受一個 UInt8 的值,所以這個構造器就用存在的 UInt8 創建了一個新的 UInt16.這里你不能隨便傳入類型-必須傳入 UInt16 構造器接受的值。
15.整數和浮點數轉換
整數和浮點數之間的轉換必須是顯示的:
let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi 等于 3.14159, 推斷為 Double 類型
這里,常量 three 用來創建新的 Double 數值,現在加號兩邊的類型是一樣的,所以可以相加。如果沒有這里的轉換,加法是不允許的。
浮點數轉整數也必須是顯示進行的。一個整形可以用 Double 或者 Float 的值來初始化:
let integerPi = Int(pi)
// integerPi 等于 3, 類型推斷為 Int
用這種方式初始化一個新的整形數值,浮點數會被截斷。意思就是 4.75 會變成 4, -3.9 會變成 -3.
16.類型別名
type aliases 為已知類型定一個別名。定義類型別名使用 typealias 關鍵字。
類型別名在你想通過名字調用一個已知類型的時候很有用,這種名字在上下文中更合適,比如使用指定大小的外部數據時:
typealias AudioSample = UInt16
定義好類型別名后,你可以在任何使用原名的地方使用它:
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound 現在是 0
這里,AudioSample 就是 UInt16 的別名。因為是別名,調用 AudioSample.min 實際上就是調用 UInt16.min,這給 maxAmplitudeFound 變量提供一個初始值 0.
17.布爾值
Swift 有個一基礎布爾類型, 叫做 Bool. 布爾值用作邏輯調用,因為它只能是 true 或者 false. Swift 提供了兩個布爾常量值, true 和 false
let orangesAreOrange = true
let turnipsAreDelicious = false
orangesAreOrange 和 turnipsAreDelicious 的類型被推斷是 Bool. 同上面的 Int 和 Double 一樣,你不需要聲明常量或者變量為 Bool.
布爾值在if 語句這種條件語句中尤其有用:
if turnipsAreDelicious {
print("Mmm, tasty turnips!")
} else {
print("Eww, turnips are horrible.")
}
// 打印 "Eww, turnips are horrible."
Swift 類型安全防止非布爾值被替換成布爾值。下面的例子報一個編譯錯誤:
let i = 1
if i {
// 這個例子不會編譯, 它會報錯
}
替代的例子如下:
let i = 1
if i == 1 {
// 這個例子可以成功編譯
}
i==1 比較的結果是 Bool 類型,所以第二個例子通過了類型檢查。
和其他類型安全例子一樣,這個方法避免了突發的錯誤,確保特別代碼塊推斷總是清晰的。
18.元組
Tuples 包含多值到一個單獨的組合值。元組里的值可以是任何類型,彼此之間可以是不同類型。
在這個例子中, (404, “Not Found”) 是一個元組,代表 Http 的錯誤碼。一個 Http 錯誤碼是訪問網頁時網頁服務器返回的特殊數值。如果你請求的網頁不存在,就會返回 404 Not Found 的錯誤碼。
let http404Error = (404, "Not Found")
// http404Error 是 (Int, String)類型, 等于 (404, "Not Found")
(404, “Not Found”) 元組包含了一個 Int 和一個 String,一個數字和一個人工可讀的描述。它可以描述成一個類型為(Int, String)的元組。
你可以用任何變化的類型創建元組,你可以按照需要要創建不同的元組。
你可以分解元組到分離的常量或者變量,通常你會訪問它們:
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
// 打印 "The status code is 404"
print("The status message is \(statusMessage)")
// 打印 "The status message is Not Found"
如果你只需要元組中的某些值,當你分解元組的時候可以用下劃線替換忽略的部分:
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// 打印 "The status code is 404"
或者,通過下標訪問元組中單獨的元素值:
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"
你可以在元組定義時,給單獨的原色命名:
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"
20.可選類型
當一個值可能缺失的時候使用可選類型。一個可選值包含兩種可能:或者有一個值,你可以通過拆包訪問這個值,或者根本沒有值。
這里有一個例子,說明可選類型如何在值缺失的時候使用。Swift 的 Int 類型有一個構造器,可以把 String 轉換為 Int 值。不過,不是每一個字符串都可以轉換的?!?23” 可以轉換為 123,但是 “helloworld” 就不可以。
下面的例子使用構造器去轉換 String :
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber 被推斷為 "Int?" 類型, 或者說 "可選的 Int"
因為構造器有可能失敗,所以它返回一個可選的 Int,而不是 Int. 一個可選 Int 寫作 Int,不是 Int. 問號表示包含的值是可選的,意思就是它可能包含某個 Int 值,也可能不包含任何值。
21.nil
通過賦值 nil 來把可選值設置成無值狀態:
var serverResponseCode: Int? = 404
// serverResponseCode 包含一個實際的整數值 404
serverResponseCode = nil
// serverResponseCode 現在沒有值
如果定義一個可選變量,但是沒有提供默認值,這個變量會被自動設置為 nil:
var surveyAnswer: String?
// surveyAnswer 自動設置為 nil
22.if 語句和強制拆包
用 if 語句,通過與 nil 做比較,你可以知道一個可選項是否包含一個值,用(==)或者(!=)來做比較。如果一個可選項有值,那么它不等于 nil:
if convertedNumber != nil {
print("convertedNumber contains some integer value.")
}
// 打印 "convertedNumber contains some integer value."
一旦你確定可選項包含一個值,你可以通過在可選項的名字后面加(!)來獲取它的值。這個感嘆號是說:我知道這個可選項一定有值;請使用它!下面是強制拆包獲取可選項的值:
if convertedNumber != nil {
print("convertedNumber has an integer value of \(convertedNumber!).")
}
// 打印 "convertedNumber has an integer value of 123."
23.可選綁定
使用可選綁定找出可選項是否包含一個值。如果要這樣做,使這個值作為一個臨時常量或者變量??蛇x綁定用 if 和 while 語句來判斷可選項的值,然后提取這個值到常量或者變量,作為操作的一部分。
用 if 語句像下面這樣寫:
if let constantName = someOptional {
statements
}
你可以重寫 possibleNumber 實例,通過使用可選綁定而不是強制拆包:
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 包含一個值,設置一個新的常量叫 actualNumber,它的值就是包含在可選項中的值。
如果轉換成功,actualNumber 常量變的可用,執行第一個分支的語句。因為已經初始化了可選項的值,所以不需要用感嘆號去拆包。
你可以同時使用常量和變量,如果你想操作 if 語句第一個分支里的 actualNumber,你可能要寫 if var actualNumber 來替換代碼,然后可選項的值會變成變量值而不是常量值。
你可以在單獨的 if 語句包括盡可能多的可選綁定和布爾條件,用逗號分開即可。如果任何可選項的值為 nil 或者布爾條件等于 false,if 條件被認為是 false. 下面的 if 語句是一樣的:
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
// 打印 "4 < 42 < 100"
if let firstNumber = Int("4") {
if let secondNumber = Int("42") {
if firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
? ? }?
? }
}
// 打印 "4 < 42 < 100"
24.隱式拆包可選項
如上所述,可選項表示一個常量或者變量可以沒有值。可選項可以用 if 語句判斷是否存在值,如果不存在,可以有條件用可選綁定拆包來訪問可選項的值。
有時候,通過程序的結構可以很清楚的知道可選項有值,然后這個值被第一次設置。這種情況,就不需要每次都判斷和拆包了,因為可以安全的假設總是有值。
這種可選項定義為隱式拆包可選項。隱式拆包可選項的寫法是,在類型后面假設感嘆號而不是問號。
當可選項的值在首次定義后就能確定存在的事,隱式拆包可選項很有用。隱式拆包可選項主要用在類的初始化。
隱式拆包可選項在這種場景下,是正??蛇x項,但是也可以用作一個非可選項,無需每次訪問都拆包。下面的例子展示了,可選字符串和隱式拆包可選項字符串作為顯示 String 時訪問它們包含的值的不同行為。
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要一個感嘆號
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要感嘆號
你依然可以把隱式拆包可選項當做正常的可選項,來判斷它是否包含一個值:
if assumedString != nil {
print(assumedString)
}
// 打印 "An implicitly unwrapped optional string."
你可以結合可選綁定來使用隱式拆包可選項,然后在一行語句中判斷和拆包它的值:
if let definiteString = assumedString {
print(definiteString)
}
// 打印 "An implicitly unwrapped optional string."
25.錯誤處理
使用錯誤處理響應程序執行中錯誤條件。
與可選項做對照,可選項用有無值來表示一個函數的成功失敗,錯誤處理允許你檢測遷走的失敗原因,同時如果必要的話,會把錯誤傳遞到程序的另外一部分。
當函數遇到一個錯誤情況,它就會 throws 一個錯誤。函數調用者可以捕獲這個錯并正確響應。
func canThrowAnError() throws {
// 這個函數可能會拋出一個錯誤
}
一個函數表明它可以通過在定義中包含 throws 關鍵詞來拋出一個錯誤。當你調用這個可以拋出錯誤的函數時,你要準備 try 關鍵詞。
Swift 自動把錯誤往外拋知道它被一個 catch 語句處理。
do {
try canThrowAnError()
// 無錯誤拋出
? } catch {
// 拋出一個錯誤
}
do 語句創建了一個代碼塊,它允許錯誤可以傳遞給一個或者多個 catch 項。
這里有一個列子,展示響應不同錯誤條件的錯誤處理方式:
func makeASandwich() throws {
// ...
? ?}
? ?do{
try makeASandwich()
eatASandwich()
? ? ?} catch SandwichError.outOfCleanDishes {
washDishes()
? ? } catch SandwichError.missingIngredients(let ingredients) {
buyGroceries(ingredients)
}
在這個例子里,如果沒有干凈的餐具可用或者任何調料確實,makeASandwich 將會拋出一個錯誤。因為 makeASandwich 可以拋出錯誤,所以函數調用包含在 try 表達式。經過 do 語句包含處理,任何拋出的錯誤都會傳遞到 catch 項。
如果沒有錯誤拋出,eatASandwich 函數會被調用。如果一個錯誤拋出然后匹配 SandwichError.outOfCleanDishes 這個條件,那么 washDishes 會被調用。如果一個錯誤拋出然后匹配 SandwichError.missingIngredients 條件,那么 buyGroceries 會被調用
26.斷言和先決條件
斷言和先決條件的判斷發生在運行時。使用它們可以保證一個條件必須滿足才會執行后面的代碼。如果斷言和先決條件的布爾值等于真, 代碼會繼續執行。如果這個條件是假, 程序的當前狀態是無效的; 代碼結束執行, 你的應用也會終止。
在編碼時, 你可以用斷言和先決條件表達假設和預期, 所以你可以在代碼里包含它們。斷言可以幫你在開發時發現錯誤和不正確的假設, 先決條件幫你在產品階段檢測問題。除了在運行時校驗你的預期, 斷言和先決條件也可以成為代碼的有效的文本形式。跟上面討論的錯誤條件不同, 斷言和先決條件不用于可恢復和預期的錯誤。因為一個失敗的斷言或者先決條件表明一個無效的程序狀態, 沒有辦法捕獲一個失敗的斷言。
使用斷言和先決條件不是代碼設計的替代, 這種方式下無效條件不太可能出現。不過, 使用它們強制有效的數據和狀態, 當無效狀態發生時, 你的程序可以預期的終止, 有助于問題的定位。停止執行可以限制無效狀態導致的破壞。
斷言和先決條件的區別是它們的判斷時間: 斷言只在調試運行時判斷, 而先決條件可以在調試和產品運行時判斷。在產品運行時, 斷言是不執行的。這就意味你可以在開發時盡量多的使用斷言, 不會影響產品的性能。
27.使用斷言調試
調用 Swift 標準庫函數 assert(::file:line:) 就可以編寫斷言。傳入判斷真假的表達式和一條信息即可。例如:
let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// 這個斷言會失敗因為 -3 不是 >= 0.
在這個例子里, 如果 age >= 0 代碼會執行執行, 就是說, 如果年齡值是正數。如果這個年齡值是負數, 就像上面的代碼那樣, age >= 0 等于假, 斷言失敗, 終止程序。
你可以省略斷言的信息—例如。
assert(age >= 0)
如果這個代碼已經判斷了條件, 你可以使用 assertionFailure(_:file:line:) 函數說明斷言已經失敗。例如:
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.")
}
28.執行先決條件
當一個條件有可能失敗時, 使用先決條件, 但是真的時候代碼必須可以繼續執行。例如, 使用一個先決條件判斷一個下標沒有越界, 或者判斷一個函數傳入了一個有效值。
調用 precondition(::file:line:) 函數就可以實現先決條件。傳入判斷真假的表達式和一條信息即可。例如:
// 一個下標實現里的代碼
precondition(index > 0, "Index must be greater than zero.")
你還可以調用 preconditionFailure(_:file:line:) 函數表明一個失敗發生—例如, switch 語句的默認分支執行, 不過所有有效輸入數據都已經被其他分支處理了。
備注 如果你以非判斷模式編譯 (-Ounchecked), 先決條件不會判斷。編譯器假設先決條件總是真, 它會相應優化你的代碼。不過, fatalError(:file:line:) 函數總會終止執行, 不管最優化設置。
你可以在原型和早期開發時, 使用 fatalError(:file:line:) 函數穿件還沒有實現的功能, 比如 fatalError(“Unimplemented”). 因為致命錯誤永遠不會優化, 跟斷言或者先決條件不同, 如果遭遇未實現的功能, 你可以保證執行停止。
29.基礎運算符
operator 是特殊的符號或者短語,用來檢查,改變,或者合并數值。比如,加號(+)用來加兩個數值,比如 let = 1 + 2,邏輯與(&&)合并兩個布爾值,比如 enteredDoorCode && passedRetinaScan
Swift 支持大部分的 C 語言操作符,并且提升了消除一般編碼錯誤的能力。賦值運算符(=)不會返回一個值,用來防止錯誤是使用(==)。算數運算符(+,-,*,/,% 等等)監測和拒絕值溢出,為了避免值溢出造成未知的結果。你可以用 Swift 的溢出運算符選擇值溢出行為。
Swift 同時提供了兩個范圍運算符(a..\<b和a…b),這在 C 語言里沒有。這些作為表達一個范圍值的快捷方式。
這個章節介紹 Swift 的普通運算符。 高級運算符包含了 Swift 的高級運算符,描述如何定義你自己的運算符,然后為你自定義的類型實現標準運算符。
30.術語
運算符是一元的,二元的,或者三元的:
一元運算符操作單一目標(比如 -a)。一元前綴操作費直接寫在目標前(比如 !b),一元后綴操作費則直接出現在目標后(比如 c?。?。
二元運算符操作兩個目標(比如 2+3),并且出現在兩個目標的中間。
三元運算符操作三個目標,跟 C 語言一樣,Swift 只有一個三元運算符,也就是三元條件運算符(a ?b :c)
運算符作用的值是操作數。在表達式 1 + 2 里,+ 號是二元運算符,兩個操作數分別是 1 和 2.
31.賦值運算符
賦值運算符(a=b)用 b 的值初始化或者更新 a 的值。
let b = 10
var a = 5
a = b
// a 等于 10
如果賦值語句的右側是多值的元組,它的元素可以一次分解為多個常量或者變量:
let (x, y) = (1, 2)
// x 等于 1, y 等于 2
這個特性防止 = 與 == 混淆。 讓 if x = y 無效, Swift 幫你避免這種錯誤。
32.算術運算符
Swift 對所有類型支持四種標準算術運算符:
加號 (+) 1 + 2 // 等于 3
減號 (-) 5 - 3 // 等于 2
乘號 () 2 3 // 等于 6
除號 (/) 10.0 / 2.5 // 等于 4.0
與 C 語言 和 Objective-C 語言不同, Swift 算術運算符默認不允許值溢出。 你可以通過 Swift 溢出運算符來選擇值溢出行為(例如 a &+ b)。
加號也支持字符串連接:
"hello, " + "world"? // 等于 "hello, world"
33.余數運算符
余數運算符 (a % b) 計算 a 中 b 的倍數,并且返回余數。
備注 余數運算符 (%) 在其他語言中作為取模運算符。 不過, 在 Swift 中對負數來說, 嚴格來講, 它是余數而不是一個取模運算。
下面演示余數運算符是如何工作的。 計算 9 % 4, 你首先算出9里有多少個4:
你可以確定9里有兩個4, 余數是 1 (橙色顯示)。
在 Swift 里, 這個會寫作:
9 % 4 // 等于 1
為了確定 a % b 的結果, % 運算符計算下面的方程式,然后返回余數作為輸出:
a = (b x 倍數) + 余數
這里倍數是a里面b的最大倍數。
把 9 和 4 代入方程式值域:
9 = (4 x 2) + 1
計算負值a的余數也是用相同的方法:
-9 % 4? // 等于 -1
活的余數 -1.
負值 b 被忽略。 意思就是 a % b 和 a % -b 結果是一樣的。
33.一元減法運算符
數值符號可以用前綴 - 連接, 也就是大家熟知的一元減法運算符:
let three = 3
let minusThree = -three? ? ? // minusThree 等于 -3
let plusThree = -minusThree? // plusThree 等于 3, 或者 "負負 3"
一元減法運算符 (-) 直接放在操作數前, 沒有空格。
34.一元加法運算符
一元加法運算符 (+) 返回操作數值, 沒有任何改變:
let minusSix = -6
let alsoMinusSix = +minusSix? // alsoMinusSix 等于 -6
盡管一元加法運算符實際上不做任何事, 你可以用它來提供對稱的代碼,當在代碼中使用正負數的時候。
35.復合賦值運算符
和 C 語言相似, Swift 提供復合賦值運算符來合并賦值 (=) 和其他操作數。 一個例子就是加法賦值運算符 (+=):
var a = 1
a += 2
// a 現在等于 3
表達式 a += 2 是 a = a + 2 的快捷方式。 實際上, 加法和賦值合并進一個操作符,同時做了兩件事。
備注 復合賦值運算符不返回值。 比如, 你不能這樣寫 let b = a += 2.
36.比較運算符
Swift 支持所有標準 C 語言比較運算符:
等于 (a == b)
不等于(a != b)
大于 (a > b)
小于 (a < b)
大于等于 (a >= b)
小于等于 (a <= b)
備注 Swift 同時提供兩個相等運算符 (=== and !==), 你可以用來測試兩個對象引用是否引用了相同的對象實例。 更多信息參考類和結構體。
每個比較運算符都返回一個布爾值,來表明語句是否是真的:
1 == 1 // 真,因為1 等于 1
2 != 1 // 真,因為2 不等于 1
2 > 1 // 真,因為2 大于 1
<1 2="" <="" 真,因為1="" 小于="" 2<="" li="">
1 >= 1 // 真,因為1大于或者等于1
2 <= 1 // 假,因為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”, because name is indeed equal to “world”.
更多 if 語句, 參考控制流章節。
你可以比較含有相同數量值的元組, 只要元組里的值可以比較。 例如, Int 和 String 可以比較, 意思就是 (Int, String) 可以比較。 相反, Bool 不能比較, 意思就是包含布爾值的元組不能作比較。
元組是從左到右做比較的, 每次一個值, 直到比較發現兩個值不等為止。 這兩個被比較的值, 比較的結果決定了整個元組比較的結果。 如果所有元素都相等, 那么元組就是相等的。例如:
(1, “zebra”) < (2, “apple”) // 真,因為1小于2; “zebra” 和 “apple” 不比較
(3, “apple”) < (3, “bird”) // 真,以為3等于3, “apple” 小于 “bird”
(4, “dog”) == (4, “dog”) // 真,因為4等于4, “dog” 等于 “dog”
上面這個例子, 在第一行你可以看見從左到右比較的行為。 因為1小于2, (1, “zebra”) 被認為小于 (2, “apple”), 不用管元組里的其他值。 不管 “zebra” 是不是小于 “apple”, 因為比較已經取決于元組里的第一個元素了。 不過, 當元組第一個元素相等的時候,比較就像第二行,第三行發生的那樣。
# 三元條件運算符
三元條件運算符是含有三部分的特殊運算符, 樣式是 問題 ? 答案1 : 答案2。 這是基于問題是真假對表達式之一的判斷。 如果問題是真, 求 answer1 的值并返回; 否則, 求 answer2 的值并返回。
三元條件運算符是下面代碼的簡寫:
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
let rowHeight: Int
if hasHeader {
rowHeight = contentHeight + 50
} else {
rowHeight = contentHeight + 20
}
// rowHeight 等于 90
第一個例子使用三元條件運算符,意味著行高可以在一行代碼里正確設置, 這個比第二個例子的代碼要簡潔。
三元條件運算符提供一個簡寫,來決定使用兩個表達式中的哪一個。 小心使用三元條件運算符。 過度使用,代碼就是很難理解。 盡量避免把三元條件運算符的多個實例合并到一個符合語句。
# Nil-聯合運算符
nil-聯合運算符 (a ?? b) 展開一個可選項 a 如果它包含一個值的話, 或者返回一個默認值 b 如果 a 是 nil。 表達式 a 總是一個可選類型。 表達式 b 必須匹配存儲在 a 里的值類型。
nil-聯合運算符是下面代碼的簡寫:
a != nil ? a! : b
上面的代碼使用三元條件運算符,然后強制展開 (a!) 來獲取 a 中的值,如果這個值不空的話, 否則返回 b。nil-聯合運算符提供了更優雅簡潔的方式來壓縮這個條件判斷和展開。
> 備注 如果值非空, b 的值不會得到。 這就是人們熟知的短路估算。
下面的例子使用 nil-聯合運算符在默認顏色名稱和可選用戶定義的顏色名稱之間做選擇:
let defaultColorName = “red”
var userDefinedColorName: String? // 默認是 nil
var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 是 nil, 所以colorNameToUse 設置成默認值 “red”
userDefinedColorName 變量定義成可選的 String, 默認值是 nil. 因為 userDefinedColorName 是個可選類型, 你可以使用nil-聯合運算符來獲取它的值。 上面的例子, 運算符用來決定變量 colorNameToUse 的一個初始值。 因為 userDefinedColorName 是 nil, userDefinedColorName ?? defaultColorName 表達式返回了 defaultColorName 的值, 或者 “red”.
如果你給 userDefinedColorName 賦了一個非空值,然后執行 nil-聯合運算符再次判斷, userDefinedColorName 中包含的值就取代了默認值:
userDefinedColorName = “green”
colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName is not nil, so colorNameToUse is set to “green”
# 范圍運算符
Swift 有兩個范圍運算符, 是表達值范圍的簡寫。
# 閉合區間運算符
閉合區間運算符 (a…b) 定義從a到b的范圍, 包含 a 和 b 的值。a 的值不能比 b 大。
閉合區間運算符在范圍迭代很有用,特別是你想使用所有的值的時候。例如 for-in 循環:
for index in 1…5 {
print(“(index) times 5 is (index * 5)”)
}
// 1 乘以 5 等于 5
// 2 乘以 5 等于 10
// 3 乘以 5 等于 15
// 4 乘以 5 等于 20
// 5 乘以 5 等于 25
更多 for-in 循環, 參考控制流。
# 半開區間運算符
半開區間運算符 (a..\
邏輯非 (!a)
邏輯與 (a && b)
邏輯或 (a || b)
37.邏輯非運算符
邏輯非運算符 (!a) 反轉布爾值,這樣真就變成假, 假變成了真。
邏輯非運算符是個前綴運算符, 直接寫在操作數前面, 沒有任何空格。 可以讀作 “not a”, 下面的例子可以看到:
let allowedEntry = false
if !allowedEntry {
print("ACCESS DENIED")
}
// 打印 "ACCESS DENIED"
語句 if !allowedEntry 可以讀作 “if not allowed entry.” 后面一行僅執行 “not allowed entry” 是 true; 也就是, if allowedEntry 是 false.
在這個例子中, 謹慎定義布爾常量和變量名,可以讓代碼具有可讀性和簡潔性, 同時避免雙重否定或者混亂的邏輯語句。
38.邏輯與運算符
邏輯與運算符 (a && b) 創建邏輯表達式,這個表達式中兩個值為真,表達式也要為真。
如果任何一個值為假, 表達式的結果也將是假。 事實上, 如果第一個值為假, 第二個值不會再計算, 因為它不會讓所有表達式都等于真。 這就是人們熟知的短路估值。
這個例子有兩個布爾值,只有兩個值都是真的時候才允許訪問:
let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 打印 "ACCESS DENIED"
39.邏輯或運算符
邏輯或運算符 (a || b) 是兩個豎線組成的中間運算符。 用來創建邏輯表達式,這個表達式中只要有一個值為真,表達式的結果就是真。
跟上面邏輯與類似, 邏輯或使用短路估值去計算表達式。 如果邏輯或的左側是真, 右側就不再估值, 因為它不會改變整個表達式的結果。
下面的例子, 第一個布爾值 (hasDoorKey) 是假, 但是第二個布爾值 (knowsOverridePassword) 是真。 因為一個值為真, 表達式結果就是真, 所以允許訪問:
let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 打印 "Welcome!"
40.合并邏輯運算符
你可以合并多個邏輯運算符來創建更長的復合表達式:
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
print("Welcome!")
?} else {
print("ACCESS DENIED")
?}
// 打印 "Welcome!"
這個例子使用了多個 && 和 || 運算符來創建一個更長的復合表達式。 不過, && 和 || 運算符依然只能操作兩個值, 所以,這實際上是三個小表達式鏈接起來的。
備注 Swift 邏輯運算符 && 和 || 是左聯想的, 意思就是多個邏輯運算符的復合表達式,首先計算最左邊的子表達式
41.顯示括號
雖然不是嚴格需要,但是包含括號還是有用的, 使得復雜表達式的意圖很容易理解。 上述實例, 給第一部分加上括號很有用,會讓它的意圖很明顯:
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 打印 "Welcome!"
括號讓第一部分作為獨立可能的狀態,這樣在整個邏輯中就很清晰。 符合表達式的輸出不變, 但是整體意圖很清晰。 比起簡潔,可讀性是首選的。使用括號會讓你的意圖清晰明了。
42.字符串和字符
字符串是字符集合, 比如 “hello, world” 或者 “albatross”. Swift 字符串用 String 類型表示。 字符串內容有多種訪問方法, 包括字符值的集合。
Swift 的 String 和 Character 類型提供一個快速的, Unicode 方式來作用于你的文本。 字符串創建和操作的語法是輕量和可讀的, 字面語法跟 C 類似。 字符串連接和使用加號運算符一樣簡單, 字符的可變性由選擇常量還是變量來管理, 就像 Swift 中其他值。 你還可以使用字符串把常量,變量,字面量,和表達式插入更長的字符串, 在一個眾所周知的字符串插值。 這讓顯示自定義字符串變得容易。
盡快語法簡單, Swift 的 String 類型是個快速, 現代化的字符串實現。 每個字符串有獨立編碼的 Unicode 字符組成, 提供用多種Unicode 形式訪問這些字符的支持。
43.字符串字面量
你可以在字符串字面量中包含預定義的字符串值。一個字符串字面量就是用雙引號包起來的字符序列。
使用字面量作為常量或者變量的初始值:
let someString = "Some string literal value"
Swift 推斷 someString 常量為 String 類型, 因為它是用一個字符串字面量進行初始化。
如果你需要分為幾行的字符串, 可以使用一個多行字符串字面量。多行字符串字面量是三個雙引號包起來的字符序列:
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."
"""
由于多行形式使用了三個雙引號而不是一個, 你可以在里面包含一個雙引號, 如上所示。如果想在多行字面里包含 “”” , 你至少要轉義一個雙引號, 使用反斜杠 (). 例如:
let threeDoubleQuotes = """
Escaping the first quote \"""
Escaping all three quotes \"\"\"
"""
在多行的形式里, 字面量包含的所有行都在打開和關閉的引號里。這個字符串開始于打開的引號 (“””) 結束于關閉的引號 (“””), 這意味著引號不是以換行開始和結束的。下面的字符串都是相同的:
let singleLineString = "These are the same."
let multilineString = """
These are the same.
"""
如果要讓多行文本以換行開始和結束的話, 可以在開始和結束分別寫一個空白行。例如:
"""
This string starts with a line feed.
It also ends with a line feed.
"""
多行字符串可以縮進來匹配周圍的代碼。關閉引號之前的空格告訴 (“””) Swift 忽略所有其他行之前的空格。例如, 盡管下面的多行字符串字面量是縮進的, 實際的字符串行并不是以空格開始的。
func generateQuotation() -> String {
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."
"""
return quotation
}
print(quotation == generateQuotation())
// 打印 "true"
不過, 除了結束引號之前的空格之外, 你寫在行前的空格會被字面量包含進去。
44.初始化空字符串
創建空字符串值作為更長字符串的起點, 或者給一個變量賦一個空字符串值, 或者用初始化方法來初始化一個新的字符串實例:
var emptyString = ""? ? ? ? ? ? ? // 空字面量
var anotherEmptyString = String()? // 構造語法
// 這兩個字符串都是空的, 彼此相等
判讀一個字符串是否為空,可以使用它的布爾屬性 isEmpty:
if emptyString.isEmpty {
print("Nothing to see here")
}
// 打印 "Nothing to see here"
45.字符串易變性
你來指定一個特定字符串能不能改變(或者突變),這種變化通過把它賦給一個變量實現。 (這種情況它是可以改變的), 或者賦值給一個常量實現 (這種情況它不能被改變):
var variableString = "Horse"
variableString += " and carriage"
// variableString 等于 "Horse and carriage"
let constantString = "Highlander"
constantString += " and another Highlander"
// 報編譯期錯誤 - 常量不能修改
46.字符串是值類型
Swift 的字符串類型是值類型。 如果你創建了一個新的字符串值, 這個字符串值在傳給函數或者方法的時候被復制, 或者當它被賦值給一個常量或者變量的時候。這兩種情況, 已存在字符串值的拷貝被創建, 新的拷貝被傳遞或者賦值, 而不是原來的字符串。 值類型在結構體和枚舉是值類型中有描述。
很清楚你擁有精確的字符串值,而不用關心它從哪里來的。你可以確信傳給你的字符串不會被改變,除非你自己改變它。
在幕后, Swift 的編譯器優化了字符串的使用,這使得實際的拷貝只有真正需要的時候才會占用空間。 這就意味你使用字符串作為值類型總可以獲得高性能。
47.使用字符
你可以用 for-in 循環語句,通過它的字符屬性來迭代訪問每一個單獨的字符:
for character in "Dog!??".characters {
print(character)
}
// D
// o
// g
// !
// ??
或者, 你可以創建一個單獨的字符常量或者變量,這變量通過提供一個字符類型注釋的單字符字符串值:
let exclamationMark: Character = "!"
字符串的值可以, 可以通過把字符數組傳入它的構造器來構造:
let catCharacters: [Character] = ["C", "a", "t", "!", "??"]
let catString = String(catCharacters)
print(catString)
// 打印 "Cat!??"
48.連接字符串和字符
字符串值可以通過加號連接成新的字符串:
let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2
// welcome 現在等于 "hello there"
你也可以通過賦值運算符(+=)把字符串添加到已存在的字符串變量:
var instruction = "look over"
instruction += string2
// instruction 等于 "look over there"
你可以使用 append() 方法向字符串后面添加一個字符:
let exclamationMark: Character = "!"
welcome.append(exclamationMark)
// welcome 等于 "hello there!"
49.字符串插入
String 字符串插入是構建新字符串的一種方法,這種方法通過混合常量,變量,字面量,表達式中的字面量來實現。每個你要插入的值都包括在括弧里,前面是一個反斜杠:
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message is "3 times 2.5 is 7.5"
上面這個例子, multiplier 的值以 \(multiplier) 的形式插入到一個字符串。 這個占位符會被 multiplier 的實際值取代,當插值被計算創建新字符串的時候。
multiplier 的值是字符串后面大表達式的一部分。 這個表達式計算 Double(multiplier) 2.5 的值,然后把結果 (7.5) 插入字符串。 這種情況下, 當它被包括進字符串字面量的時候,表達式就寫作 \(Double(multiplier) 2.5).
50.Unicode
Unicode 是在不同寫作體系中用作編碼,表達,處理文字的一種國際標準。 讓你可以用一種標準形式表示任何語言的任何字符, 在諸如文本文件或者網頁等外部資源,去讀寫這些字符。Swift 的字符串和字符是完全 Unicode 編譯的。
51.Unicode 標量
背后, Swift 的本地字符串類型建立于 Unicode 標量值。對應字符或者修飾符來說, 一個 Unicode 標量是唯一的一個21位數字, 例如 U+0061 是 LATIN SMALL LETTER A (“a”), 或者 U+1F425 是 FRONT-FACING BABY CHICK (“??”).
注意并非所有 21-位 Unicode 標量都會被賦值給一些給未來預留的字符標量。 分配給字符的標量通常有一個名字, 比如 LATIN SMALL LETTER A 和 FRONT-FACING BABY CHICK.
52.字符串字面量里的特殊字符
字符串字面量可以包括下面的特殊字符:
轉義字符 \0(空字符), \(反斜杠), \t (水平制表符), \n (換行), \r (回車), \” (雙引號) 和 \’ (單引號)
任意一個 Unicode 標量, 寫作 \u{n}, n 是 一個 1–8 位十六進制數字,這個數字有個和 有效的 Unicode 編碼點相等的值。
下面四個例子顯示了特殊字符的編碼。wiseWords 常量包括兩個轉義的雙引號字符。dollarSign, blackHeart, 和sparklingHeart 常量展示了 Unicode 標量的格式:
let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
// "Imagination is more important than knowledge" - Einstein
let dollarSign = "\u{24}"? ? ? ? // $,? Unicode scalar U+0024
let blackHeart = "\u{2665}"? ? ? // ?,? Unicode scalar U+2665
let sparklingHeart = "\u{1F496}" // ??, Unicode scalar U+1F496
53.擴展字形集
Swift 的每個字符類型的實例帶包一個單獨的擴展字形集。 一個擴展字形集是一個或者多個 Unicode 標量,這些標量(當合并時)生成一個人可讀的字符。
這里有個例子。 字符 é 可以表示成單獨的 Unicode 標量 é (LATIN SMALL LETTER E WITH ACUTE<, 或者 U+00E9). 不過, 相同的字母也可以表示成標量集的一部分,字母 e (LATIN SMALL LETTER E, 或者 U+0065), 后面跟著 COMBINING ACUTE ACCENT 標量 (U+0301).COMBINING ACUTE ACCENT 標量 應用于之前的標量, 在一個 Unicode 識別 文本渲染系統渲染的時候,把 e 變成 é.
在這兩個例子里, 字符 é 表示為一個單獨的 Swift 字符值,這個值表示一個擴展字形集。 第一個例子, 集合包含一個單獨的標量; 第二個例子, 是兩個標量的集合:
let eAcute: Character = "\u{E9}"? ? ? ? ? ? ? ? ? ? ? ? // é
let combinedEAcute: Character = "\u{65}\u{301}"? ? ? ? ? // e followed by ?
// eAcute is é, combinedEAcute is é
擴展字形集是一種靈活的方式去表示很多復雜腳本字符作為單獨字符值。 比如, 來自朝鮮字母的朝鮮語音節可以表示成復合或者分離的序列。 在 Swift 里,這些表達都是合格的字符值:
let precomposed: Character = "\u{D55C}"? ? ? ? ? ? ? ? ? // ?
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}"? // ?, ?, ?
// precomposed is ?, decomposed is ?
擴展字形集使得封閉標志的標量 (比如 COMBINING ENCLOSING CIRCLE, 或者 U+20DD) 可以裝入其他 Unicode 標量作為一個單獨字符值的一部分:
let enclosedEAcute: Character = "\u{E9}\u{20DD}"
// enclosedEAcute is é?
Unicode 局部指示器符號標量可以成對組合來生成一個單獨的字符值, 比如 REGIONAL INDICATOR SYMBOL LETTER U (U+1F1FA) 和 REGIONAL INDICATOR SYMBOL LETTER S (U+1F1F8) 的組合:
let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
// regionalIndicatorForUS is ????