1. 程式人生 > Android開發 >Swift5.2-基礎(中文檔案)

Swift5.2-基礎(中文檔案)

引言

繼續學習Swift檔案,從上一章節:開篇 ,我們瞭解Swift基本的知識點,現在我們還是從詳細的基礎知識開始,不要認為基礎知識不重要,這是掌握一門開發語言的基本。由於篇幅較長,這裡分篇來記錄,接下來,開始吧!

如果你已經掌握了Swift的基礎,那麼請參閱下一章節:基本操作

基礎

Swift是一門新的針對iOS、macOS、watchOS和tvOS開發的程式語言。儘管如此,根據您使用C和Objective-C進行開發的經驗,您將熟悉Swift的許多部分。

Swift提供了自己版本的所有基本C和Objective-C型別,包括Int對應整型,Double和Float對應浮點型別,Bool對應布林型別,和String對應文字資料。Swift還提供了三種主要收藏型別的強大版本,Array,Set和Dictionary,定義在 

Collection Types.

像C一樣,Swift使用變數來儲存和通過一個標識名稱引用值。Swift還大量使用了不能更改值的變數。這些被稱為常量,比c中的常量功能強大得多。當你處理不需要更改的值時,在Swift中使用常量使程式碼更安全、更清晰。

除了熟悉的型別,Swift還引入了Objective-C中沒有的高階型別,比如元組。元組使您能夠建立和傳遞值分組。可以使用元組將函式中的多個值作為一個複合值返回。

Swift還引入了可選型別,用於處理缺少值的情況。可選值要麼說“有一個值,它等於x”,要麼說“根本沒有值”。使用可選值類似於在Objective-C中對指標使用nil,但它們適用於任何型別,而不僅僅是類。option不僅比Objective-C中的nil指標更安全、更有表現力,而且是Swift許多最強大功能的核心。

Swift是一種型別安全的語言,這意味著該語言可以幫助您清楚地瞭解程式碼可以使用的值的型別。如果您的部分程式碼需要String,型別安全防止您錯誤地將其傳遞為Int。同樣,型別安全可以防止您意外地將可選String傳遞給需要非可選String的程式碼段。型別安全可以幫助您儘早捕獲和修復開發過程中的錯誤。

1 常量和變數

常量和變數將名稱(如maximumnumberoflogintries或welcomeMessage)與特定型別的值(如數字10或字串“Hello”)關聯起來。常量的值一旦設定就不能更改,而變數可以在將來設定為不同的值。

1.1 定義常量和變數

常量額變數在使用前必須宣告。可以用let

關鍵字宣告常量,用var關鍵字宣告變數。下面是一個如何使用常量和變數來跟蹤使用者嘗試登入的次數的例子:

let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
複製程式碼

這段程式碼意思是: “定義一個maximumNumberOfLoginAttempts常量,並賦值為10。定義一個currentLoginAttempt變數,並賦值為0.”

在這個例子中,宣告瞭一個最大允許登入次數的常量,因為這個最大值沒有被修改。當前登入嘗試計數器被宣告為一個變數,因為在每次登入嘗試失敗後,該值必須遞增。

你可以在一行裡宣告多個常量和多個變數,用逗號分隔:

var x = 0.0,y = 0.0,z = 0.0
複製程式碼

注意 如果程式碼中儲存的值不會改變,請始終使用let關鍵字將其宣告為常量。變數只用於儲存需要更改的值。

1.2 型別註解

當你宣告常量和變數時,可以新增型別,這樣就可以知道需要儲存的值的型別。通過在常量或變數名後面加上冒號、空格和要使用的型別名來編寫型別註釋。

這個示例為一個名為welcomeMessage的變數提供了一個型別註釋,以表明該變數可以儲存字串值:

var welcomeMessage: String
複製程式碼

宣告中的冒號表示“…of type…”,因此上面的程式碼可以理解為:

“定義一個名稱叫welcomeMessage的字串變數。”

短語“of type String”意味著“可以儲存任何字串值”。把它看作是可以儲存的“事物的型別”(或“事物的種類”)的意思。

welcomeMessage變數可以設定為任何字串值:

welcomeMessage = "Hello"
複製程式碼

你可以在一行中定義多個相同型別的相關變數,用逗號分隔,最後的變數名後面有一個型別註釋:

var red,green,blue: Double
複製程式碼

實際上,很少需要編寫型別註釋。如果在定義常量或變數時提供初始值,Swift幾乎總是可以推斷該常量或變數使用的型別,如 型別安全和型別推斷 所述。在上面的welcomeMessage示例中,沒有提供初始值,因此welcomeMessage變數的型別是通過型別註釋指定的,而不是從初始值推斷出來的。

1.3 命名常量和變數

常量和變數的名稱支援幾乎所有字元,包括Unicode字元:

let π = 3.14159
let 你好 = "你好世界"
let ?? = "dogcow"
複製程式碼

常量個變數的名稱不能包含空格數學符號箭頭私有Unicode標量值,或**-box-drawing**字元。也不能以數字開頭,儘管數字可以包含在名稱的其他地方。

一旦宣告瞭一個某些型別的常量和變數,就不能宣告和它相同名稱的,或者改變它儲存的型別。儘管可以將一個常量修改為變數或者一個變數修改為常量。

注意 如果您需要為一個常量或變數提供與保留的Swift關鍵字相同的名稱,在使用該關鍵字作為名稱時使用反引號(`)。但是,除非別無選擇,否則要避免使用關鍵字作為名稱。

可以將現有變數的值更改為另一個相容型別的值,例如:

var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcome is now "Bonjour!"
複製程式碼

和變數不同的是,常量一旦設定了值,就不能再修改,如果強行修改會報錯:

let languageName = "Swift"
languageName = "Swift++"
// This is a compile-time error: languageName cannot be changed.
複製程式碼

1.4 列印常量和變數

可以通過**print(_:separator:terminator:)**函式列印常量或變數:

print(friendlyWelcome)
// Prints "Bonjour!"
複製程式碼

**print(:separator:terminator:)**函式是一個全域性函式,它將在適當的輸出中列印一個或多個值。在Xcode中,例如,**print(:separator:terminator:)**函式會在Xcode的控制檯上輸出列印的值。 separator和 terminator有預設值,因此當呼叫這個函式時可以忽略它們。預設情況下,函式通過新增換行符來終止它列印的行。若要列印值後不帶換行符,請傳遞一個空字串作為終止符--例如: print(someValue,terminator: "")。有關具有預設值的引數的資訊,參閱 Default Parameter Values

Swift使用字串插值將常量或變數的名稱作為佔位符包含在較長的字串中,並提示Swift將其替換為該常數或變數的當前值。用括號括起名字,在括號前面用反斜槓轉義:

print("The current value of friendlyWelcome is \(friendlyWelcome)")
// Prints "The current value of friendlyWelcome is Bonjour!"
複製程式碼

注意 字串插值可以使用的所有選項在String Interpolation都有描述。

2 註釋

使用註釋將不可執行文字包含在程式碼中,作為對自己的提示或提醒。程式碼編譯時,Swift編譯器會忽略註釋。

Swift中的註釋和c的很相似。單行註釋用雙斜槓(//):

// This is a comment.
複製程式碼

多行只是可以用/* ... */:

/* This is also a comment
but is written over multiple lines. */
複製程式碼

與C中的多行註釋不同,Swift中的多行註釋可以巢狀在其他多行註釋中。您可以通過啟動一個多行註釋塊,然後在第一個註釋塊中啟動第二個多行註釋來編寫巢狀註釋。然後關閉第二個區塊,然後關閉第一個區塊:

/* This is the start of the first multiline comment.
 /* This is the second,nested multiline comment. */
This is the end of the first multiline comment. */
複製程式碼

巢狀多行註釋使您能夠快速而輕鬆地註釋掉大塊程式碼,即使程式碼已經包含多行註釋。

3 分號

和許多其他語言不同,Swift不需要在語句末尾寫分號,儘管你希望這樣做。然而,當將多個語句寫在同一行時,需要加分號分隔:

let cat = "?"; print(cat)
// Prints "?"
複製程式碼

4 整型

整數是沒有分數成分的整數,例如42和-23。整數可以是有符號的(正、零或負)或無符號的(正或零)。

Swift提供8位、16位、32位和64位格式的有符號整數和無符號整數。這些整數遵循類似於C的命名約定,即8位無符號整數的型別為UInt8,32位有符號整數的型別為Int32。與Swift中的所有型別一樣,這些整數型別的名稱都是大寫的。

4.1 整數範圍

您可以訪問每個整數型別的最小值和最大值及其min和max屬性:

let minValue = UInt8.min  // minValue is equal to 0,and is of type UInt8
let maxValue = UInt8.max  // maxValue is equal to 255,and is of type UInt8
複製程式碼

這些屬性的值具有適當大小的數字型別(如上面示例中的UInt8),因此可以在表示式中與其他相同型別的值一起使用。

4.2 Int

在大多數情況下,您不需要在程式碼中選擇特定大小的整數。Swift提供了一個額外的整數型別Int,它與當前平臺的本地字大小相同:

  • 在32位平臺上,Int大小和Int32相同。
  • 在64位平臺上,Int大小和Int64相同。

除非你需要處理一個特定大小的整數,否則整數都要用Int定義。這有助於程式碼的一致性和互操作性。即使在32位平臺上,Int可以儲存的值在-2,147,483,648和2,647之間,這個範圍可以用在大多數整數上了。

4.3 UInt

Swift也提供了無符號的整型,UInt,它與當前平臺的本地字大小相同:

  • 在32位平臺上,UInt大小和UInt32相同。
  • 在64位平臺上,UInt大小和UInt64相同。

注意 僅當您特別需要與平臺的本地字大小相同的無符號整數型別時,才使用UInt。如果不是這樣,則首選Int,即使已知要儲存的值是非負的。整數值一致地使用Int有助於程式碼的互操作性,避免不同數字型別之間的轉換,並匹配整型型別推斷,如型別安全和型別推斷中所述。

5 Floating-浮點數

浮點數是由小數部分組成的數,如3.14159、0.1和-273.15。

與整型相比,浮點型可以代表更大範圍的值,並且可以儲存比整型更大或更小的數字。Swift提供了兩種帶符號的浮點數型別:

  • Double表示一個64位浮點數。
  • Float表示一個32位浮點數。

注意 Double的精度至少為15位十進位制數字,而Float的精度只有6位十進位制數字。要使用的適當浮點型別取決於程式碼中需要使用的值的性質和範圍。如果兩種型別都合適,則首選Double。

6 型別安全和型別推斷

Swift是一種型別安全的語言,型別安全語言鼓勵您明確程式碼可以使用的值的型別。如果程式碼的一部分需要字串,則不能錯誤地將其傳遞為Int。

因為Swift是型別安全的,它在編譯程式碼時執行型別檢查,並將任何不匹配的型別標記為錯誤。這使您能夠在開發過程中儘早捕獲和修復錯誤。

當您使用不同型別的值時,型別檢查可以幫助您避免錯誤。但是,這並不意味著必須指定宣告的每個常量和變數的型別。如果沒有指定所需值的型別,Swift會使用型別推斷來計算出適當的型別。型別推斷使編譯器在編譯程式碼時能夠通過檢查提供的值自動推斷特定表示式的型別。

由於有了型別推斷,Swift需要的型別宣告要比C或Objective-C等語言少得多。常量和變數仍然是顯式型別的,但是指定它們的型別的大部分工作已經為您完成了。

在宣告具有初始值的常量或變數時,型別推斷特別有用。這通常是通過在宣告常量或變數時為其分配一個文字值(或文字)來實現的。(文字值是直接出現在原始碼中的值,例如下面示例中的42和3.14159。)

例如,如果你給一個新常量賦值為42而沒有說明它是什麼型別,Swift推斷你希望這個常量是一個整型數,因為你用一個看起來像整數的數字初始化了它:

let meaningOfLife = 42
// meaningOfLife is inferred to be of type Int
複製程式碼

同樣地,如果你沒有指定一個浮點型文字的型別,Swift推斷你想要建立一個Double:

let pi = 3.14159
// pi is inferred to be of type Double
複製程式碼

在推斷浮點數型別時,Swift總是選擇Double(而不是Float)。

如果你在一個表示式中結合了整型和浮點型文字,Double型別會從上下文推斷出來:

let anotherPi = 3 + 0.14159
// anotherPi is also inferred to be of type Double
複製程式碼

字面值3本身沒有顯式的型別,因此可以從作為加法的一部分的浮點字面值中推斷出適當的輸出型別Double。

7 數字字面值

整型文字可以寫成:

  • 無字首的十進位制數
  • 一種二進位制數,字首為0b
  • 帶有0o字首的八進位制數
  • 帶有0x字首的十六進位制數字

所有這些字面值都有一個十進位制值17:

let decimalInteger = 17
let binaryInteger = 0b10001       // 17  二進位制
let octalInteger = 0o21           // 17  八進位制
let hexadecimalInteger = 0x11     // 17  十六進位制
複製程式碼

浮點字面值可以是十進位制(沒有字首)或十六進位制(有0x字首)。它們必須在小數點的兩邊都有一個數字(或十六進位制數)。十進位制浮點數還可以有一個可選的指數,由大寫或小寫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
複製程式碼

數值文字可以包含額外的格式,以使它們更容易閱讀。整數和浮點數都可以用額外的零來填充,並且可以包含下劃線來增強可讀性。兩種格式都不會影響文字的基本值:

let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1
複製程式碼

8 數值型別轉換

對程式碼中的所有通用整型常量和變數使用Int型別,即使已知它們是非負的。在日常情況中使用預設整數型別意味著整型常量和變數在程式碼中立即可以互操作,並將匹配整型文字值的推斷型別。

只有在手頭的任務特別需要其他整數型別時才使用它們,因為外部源顯式地調整了資料大小,或者為了效能、記憶體使用或其他必要的優化。在這些情況下使用顯式大小的型別有助於捕獲任何意外值溢位,並隱式記錄所使用資料的性質。

8.1 整數轉換

可以儲存在整型常量或變數中的數字的範圍對於每種數值型別是不同的。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 cannot store a number larger than its maximum value,// and so this will also report an error
複製程式碼

由於每個數值型別可以儲存不同範圍的值,因此必須根據具體情況選擇進行數值型別轉換。這種可選擇的方法可以防止隱藏的轉換錯誤,並有助於在程式碼中顯式地顯示型別轉換意圖。

要將一種特定的數字型別轉換為另一種,可以使用現有值初始化所需型別的新數字。在下面的示例中,常數2000的型別為UInt16,而常數1的型別為UInt8。它們不能直接相加,因為它們不是同一型別。相反,這個例子呼叫UInt16(one)來建立一個新的初始化值為one的UInt16,並使用這個值代替原來的值:

let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
複製程式碼

因為新增的兩邊現在都是UInt16型別,所以允許新增。輸出常數(twoThousandAndOne)被推斷為UInt16型別,因為它是兩個UInt16值的和。

SomeType(ofInitialValue)是呼叫Swift型別的初始化器並傳入初始值的預設方法。在後臺,UInt16有一個接受UInt8值的初始化器,因此這個初始化器用於從現有的UInt8生成一個新的UInt16。但是,您不能在這裡傳入任何型別—它必須是UInt16提供初始化器的型別。擴充套件包括擴充套件現有型別以提供接受新型別(包括您自己的型別定義)的初始化器。

8.2 整數和浮點數轉換

整數和浮點數型別之間的轉換必須明確:

let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi equals 3.14159,and is inferred to be of type Double
複製程式碼

在這裡,常量3的值用於建立一個型別為Double的新值,這樣加法的兩邊都是相同的型別。如果不進行此轉換,則不允許新增。

浮點到整數的轉換也必須明確。整數型別可以用雙值或浮點值初始化:

let integerPi = Int(pi)
// integerPi equals 3,and is inferred to be of type Int
複製程式碼

以這種方式初始化新的整數值時,浮點值總是被截斷。這意味著4.75變成4,而-3.9變成-3。

注意 組合數值常量和變數的規則與組合數值文字的規則不同。文字值3可以直接新增到文字值0.14159,因為數字文字本身沒有顯式的in和of型別。它們的型別只有在由編譯器計算時才會推斷出來。

9 型別別名

型別別名定義現有型別的替代名稱。可以通過typealias關鍵詞定義型別別名。

當你想通過上下文更合適的名稱來引用一個現有的型別時,型別別名很有用,例如當從外部來源處理特定大小的資料時:

typealias AudioSample = UInt16
複製程式碼

一旦定義型別別名,你可以在任何地方使用別名,在可能使用原始的名稱的地方:

var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound is now 0
複製程式碼

這裡,AudioSample被定義為UInt16的別名。因為它是一個別名,呼叫AudioSample.min也就是呼叫UInt16.min,作為maxAmplitudeFound為0的值。

10 布林值

Swift有一個基本的布林型別,叫做Bool。布林值被稱為邏輯值,因為它們只能是真或假。Swift提供了兩個布林常量值true和false:

let orangesAreOrange = true
let turnipsAreDelicious = false
複製程式碼

orangesAreOrange和turnipsAreDelicious的型別已經被推斷為Bool,因為它們是用布林文字值初始化的。與上面的Int和Double一樣,如果在建立常量或變數時就將它們設定為true或false,則不需要將它們宣告為Bool。當Swift用其他已知型別的值初始化常量或變數時,型別推斷有助於使Swift程式碼更加簡潔和可讀。

布林值在處理條件語句時特別有用,比如if語句:

if turnipsAreDelicious {
    print("Mmm,tasty turnips!")
} else {
    print("Eww,turnips are horrible.")
}
// Prints "Eww,turnips are horrible."
複製程式碼

條件語句(如if語句)在控制流 中有更詳細的介紹。

Swift的型別安全防止非布林值被Bool替換。下面的示例報告了編譯時錯誤:

let i = 1
if i {
    // this example will not compile,and will report an error
}
複製程式碼

但是,下面的另一個例子是有效的:

let i = 1
if i == 1 {
    // this example will compile successfully
}
複製程式碼

i == 1比較的結果是Bool型別,因此第二個示例通過了型別檢查。像i == 1這樣的比較在基本運運算元中討論。

與Swift中的其他型別安全示例一樣,這種方法避免了意外錯誤,並確保特定程式碼段的意圖始終清晰。

11 元組

元組將多個值分組為一個複合值。元組中的值可以是任何型別,不必彼此具有相同的型別。

在本例中,(404,“Not Found”)是描述HTTP狀態程式碼的元組。HTTP狀態碼是web伺服器在請求web頁面時返回的特殊值。如果你請求的網頁不存在,它會返回404 Not Found狀態碼。

let http404Error = (404,"Not Found")
// http404Error is of type (Int,String),and equals (404,"Not Found")
複製程式碼

元組將一個Int和一個字串組合在一起,為HTTP狀態碼提供兩個單獨的值:一個數字和一個可讀的描述。它可以被描述為“型別(Int,String)的元組”。

您可以根據型別的任意排列建立元組,它們可以包含任意多的不同型別。沒有什麼可以阻止您擁有型別(Int,Int,Int)或(String,Bool)的元組,或者您需要的任何其他置換。

您可以將元組的內容分解為單獨的常量或變數,然後像往常一樣訪問它們:

let (statusCode,statusMessage) = http404Error
print("The status code is \(statusCode)")
// Prints "The status code is 404"
print("The status message is \(statusMessage)")
// Prints "The status message is Not Found"
複製程式碼

如果你只需要一些元組的值,當你分解元組的時候,用一個下劃線(_)忽略部分元組:

let (justTheStatusCode,_) = http404Error
print("The status code is \(justTheStatusCode)")
// Prints "The status code is 404"
複製程式碼

或者,使用從0開始的索引號訪問元組中的單個元素值:

print("The status code is \(http404Error.0)")
// Prints "The status code is 404"
print("The status message is \(http404Error.1)")
// Prints "The status message is Not Found"
複製程式碼

當元組被定義時,您可以命名元組中的單個元素:

let http200Status = (statusCode: 200,description: "OK")
複製程式碼

如果你在一個元組中命名元素,你可以使用元素名來訪問這些元素的值:

print("The status code is \(http200Status.statusCode)")
// Prints "The status code is 200"
print("The status message is \(http200Status.description)")
// Prints "The status message is OK"
複製程式碼

元組作為函式的返回值特別有用。試圖檢索web頁面的函式可能會返回(Int,String)元組型別,以描述頁面檢索的成功或失敗。通過返回具有兩個不同型別值的元組,與只能返回單個型別的單個值相比,函式提供了關於其結果的更有用的資訊。有關更多資訊,請參見具有多個返回值的函式

注意 元組對於相關值的簡單組非常有用。它們不適合建立複雜的資料結構。如果資料結構可能更復雜,那麼將其建模為類或結構體,而不是元組。有關更多資訊,請參見結構體和類

12 可選值

在可能沒有值的情況下使用可選值。可選的表示兩種可能:要麼有一個值,您可以展開可選的來訪問該值,要麼根本沒有值。

注意 可選值的概念在C或Objective-C中不存在。Objective-C中最接近的是,從方法中返回nil的能力,否則會返回一個物件,nil表示“缺少一個有效物件”。但是,這隻適用於物件,而不適用於結構體、基本C型別或列舉值。對於這些型別,Objective-C方法通常返回一個特殊的值(比如NSNotFound)來表示沒有值。這種假設方法的呼叫者知道有一個特殊的值要進行測試,並且記得檢查它。Swift的可選值可以讓你指示任何型別的值是否存在,而不需要特殊的常量。

下面是一個示例,說明如何使用可選值來處理缺少值的情況。Swift的Int型別有一個初始化器,它試圖將字串值轉換為Int值。但是,不是每個字串都可以轉換為整數。字串“123”可以轉換為數字值123,但是字串“hello,world”沒有明顯的數字值可以轉換。

下面的例子使用了初始化器來嘗試將一個字串轉換成Int型別:

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber is inferred to be of type "Int?",or "optional Int"
複製程式碼

因為初始化器可能失敗,它返回一個可選的整型,而不是整型。問號表示它包含的值是可選的,這意味著它可能包含一些Int值,也可能根本不包含值。它不能包含任何其他東西,比如Bool值或String值。要麼是整數,要麼什麼都不是)

12.1 nil

通過分配特殊值nil,設定一個可選的變數為無值狀態:

var serverResponseCode: Int? = 404
// serverResponseCode contains an actual Int value of 404
serverResponseCode = nil
// serverResponseCode now contains no value
複製程式碼

注意 不能對非可選常量和變數使用nil。如果程式碼中的常量或變數在某些條件下需要處理缺少值的情況,請始終將其宣告為適當型別的可選值。

如果定義了一個可選變數,並且沒有給它設值,那麼它的值預設是nil:

var surveyAnswer: String?
// surveyAnswer is automatically set to nil
複製程式碼

注意 Swift裡的nil和OC裡的不同,OC裡的nil是一個指標,指向一個不存在的物件。Swift裡的nil不是一個指標--它是某種型別的值的缺失。任何型別的選項都可以設定為nil,而不僅僅是物件型別。

12.2 If語句和強制解包

通過比較可選值和空值,可以使用if語句來確定可選值是否包含值。您可以使用“等於”操作符(==)或“不等於”操作符(!=)執行比較。

如果可選值有值,會被認為不是nil:

if convertedNumber != nil {
    print("convertedNumber contains some integer value.")
}
// Prints "convertedNumber contains some integer value."
複製程式碼

一旦確定可選值有值,可以通過在可選名稱的末尾新增感嘆號(!)來訪問其基礎值。感嘆號有效地表示:“我知道這個可選值肯定有一個值;請使用它。這被稱為可選值的強制解包:

if convertedNumber != nil {
    print("convertedNumber has an integer value of \(convertedNumber!).")
}
// Prints "convertedNumber has an integer value of 123."
複製程式碼

想要了解更多If語句,請參閱控制流

注意 試著使用!訪問不存在的可選值將觸發執行時錯誤。在使用前,一定要確保一個可選的包含一個非nil值!強制開啟它的值。

12.3 可選值繫結

您可以使用可選繫結來查明可選項是否包含值,如果包含值,則使該值作為臨時常量或變數可用。可選繫結可以與if和while語句一起使用,以檢查可選語句中的值,並將該值提取為常量或變數,這是單個操作的一部分。if和while語句在控制流中有更詳細的描述。

使用If語句寫一個如下的可選繫結:

if let constantName = someOptional {
    statements
}
複製程式碼

您可以重寫Optionals部分中的possibleNumber示例,以使用可選繫結而不是強制解包:

if let actualNumber = Int(possibleNumber) {
    print("The string \"\(possibleNumber)\" has an integer value of \(actualNumber)")
} else {
    print("The string \"\(possibleNumber)\" could not be converted to an integer")
}
// Prints "The string "123" has an integer value of 123"
複製程式碼

這段程式碼的意思是: “如果Int(possibleNumber)返回的可選整數包含一個值,則設定一個名為actualNumber的新常量為可選整數中包含的值。”

如果轉換成功,就可以在If語句的第一個分支中使用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")
}
// Prints "4 < 42 < 100"

if let firstNumber = Int("4") {
    if let secondNumber = Int("42") {
        if firstNumber < secondNumber && secondNumber < 100 {
            print("\(firstNumber) < \(secondNumber) < 100")
        }
    }
}
// Prints "4 < 42 < 100"
複製程式碼

注意 在if語句中使用可選繫結建立的常量和變數只能在if語句體中使用。相反,使用guard語句建立的常量和變數可以在guard語句後面的程式碼行中使用,如Early Exit中所述。

12.4 隱式解包可選值

如上所述,可選值表示常量或變數允許“無值”。可選值可以用if語句檢查值是否存在,也可以用可選繫結有條件地解除包裝,以訪問可選值(如果存在)。

有時候從程式結構上,可選值在第一次設定後,總會有一個值。在這些情況下,在訪問可選值的時候,移除檢查和解包的操作是有效的,因為它總是安全地假定有一個值。

這些型別的可選值被定義為隱式的解包可選值。通過在希望使型別可選的後面放置感嘆號(String!)而不是問號(String?)來編寫可選的隱式解包。在使用可選名稱時,不要在其後放置感嘆號,而應在宣告可選型別時在其後放置感嘆號。

let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation point

let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // no need for an exclamation point
複製程式碼

(注:這裡終於清楚地明白了?和!的用法,說明學一門語言還是要從官方檔案開始)

你可以將可選的隱式解包看作允許可選的物件在需要時強制解包。當你使用隱式解包裝可選值時,Swift首先嚐試將其作為普通可選值使用;如果不能作為可選值使用,Swift強制開啟該值。在上面的程式碼中,可選值assumedString在將其值分配給implicitString之前被強制解除包裝,因為implicitString具有顯式的、非可選的字串型別。在下面的程式碼中,optionalString沒有顯式型別,所以它是一個普通的可選型別。

let optionalString = assumedString
// The type of optionalString is "String?" and assumedString isn't force-unwrapped.
複製程式碼

如果一個可選的隱式解包是nil,並且您嘗試訪問它的包裝值,您將觸發一個執行時錯誤。其結果與在不包含值的普通可選項後放置感嘆號完全相同。

你可以檢查一個可選的隱式解包是否為nil,就像你檢查一個普通的可選:

if assumedString != nil {
    print(assumedString!)
}
// Prints "An implicitly unwrapped optional string."
複製程式碼

你也可以使用可選值繫結隱式解包可選的值,在一個單獨的語句中檢查和解包它的值:

if let definiteString = assumedString {
    print(definiteString)
}
// Prints "An implicitly unwrapped optional string."
複製程式碼

注意 當一個變數可能在以後變成nil時,不要使用可選值繫結隱式解包可選的值。如果需要在變數的生命週期內檢查nil值,請始終使用普通的可選型別。

13 錯誤處理

使用錯誤處理來響應程式在執行過程中可能遇到的錯誤條件。

可選值可以使用值的存在或不存在來傳遞函式的成功或失敗,與之相反,錯誤處理允許您確定失敗的根本原因,並在必要時將錯誤傳播到程式的另一部分。

當函式遇到錯誤條件時,它會丟擲錯誤。然後,函式的呼叫者可以捕獲錯誤並作出適當的響應。

func canThrowAnError() throws {
    // this function may or may not throw an error
}
複製程式碼

函式通過在其宣告中包含throws關鍵字來表示可以丟擲錯誤。當呼叫可能丟擲錯誤的函式時,應在表示式前新增try關鍵字。

Swift自動將錯誤傳播到當前範圍之外,直到它們被catch子句處理。

do {
    try canThrowAnError()
    // no error was thrown
} catch {
    // an error was thrown
}
複製程式碼

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匹配。missingcomponents情況下,使用catch模式捕獲的相關[String]值呼叫buyGroceries(_:)函式。

錯誤處理中更詳細地介紹了丟擲、捕獲和傳播錯誤。

14 斷言和先決條件

斷言和前置條件是在執行時進行的檢查。在執行任何其他程式碼之前,使用它們確保滿足必要條件。如果斷言或先決條件中的布林值為真,則程式碼執行照常繼續。如果條件計算為false,則程式的當前狀態無效;程式碼執行結束,應用程式終止。

在編碼時,可以使用斷言和先決條件來表示所作的假設和期望,因此可以將它們作為程式碼的一部分。斷言幫助您在開發期間發現錯誤和不正確的假設,而前提條件幫助您檢測生產中的問題。

除了在執行時驗證您的期望之外,斷言和先決條件也成為程式碼中有用的檔案形式。與上面錯誤處理中討論的錯誤條件不同,斷言和前置條件不會用於可恢復的或預期的錯誤。由於失敗的斷言或先決條件指示無效的程式狀態,因此無法捕獲失敗的斷言。

使用斷言和先決條件不能替代以不太可能出現無效條件的方式設計程式碼。但是,使用它們強制執行有效的資料和狀態會導致應用程式在出現無效狀態時可預測地終止,並有助於使問題更容易除錯。在檢測到無效狀態時立即停止執行還有助於限制由該無效狀態造成的損害。

斷言和前提條件的區別在於它們被檢查的時候:斷言只在除錯構建中被檢查,但是前提條件在除錯和生產構建中都被檢查。在生產構建中,不計算斷言中的條件。這意味著您可以在開發過程中使用任意數量的斷言,而不會影響生產中的效能。

14.1 用斷言除錯

通過從Swift標準庫中呼叫assert(::file:line:)函式來編寫斷言。向該函式傳遞一個計算結果為true或false的表示式,以及一條訊息,如果條件的結果為false,則顯示該訊息。例如:

let age = -3
assert(age >= 0,"A person's age can't be less than zero.")
// This assertion fails because -3 is not >= 0.
複製程式碼

在本例中,如果age >= 0的值為真(即age的值非負),則繼續執行程式碼。如果age的值是負數,如上面的程式碼所示,那麼age >= 0計算為false,斷言失敗,終止應用程式。

您可以省略斷言訊息—例如,當它只是以散文形式重複條件時。

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.")
}
複製程式碼

14.2 執行先決條件

只要條件可能為假,但必須為真,程式碼才能繼續執行,就使用前置條件。例如,使用先決條件檢查下標是否超出範圍,或檢查函式是否傳遞了有效值。

你可以通過呼叫 precondition(_:_:file:line:)函式來編寫一個先決條件。向該函式傳遞一個計算結果為true或false的表示式,以及一條訊息,如果條件的結果為false,則顯示該訊息。例如:

// In the implementation of a subscript...
precondition(index > 0,"Index must be greater than zero.")
複製程式碼

您還可以呼叫preconditionFailure(_:file:line:)函式來指示發生了故障——例如,如果採用了switch的預設情況,但是所有有效的輸入資料應該由switch的其他情況之一處理。

注意 如果以unchecked模式(-Ounchecked)編譯,則不會檢查前置條件。編譯器假設前提條件總是為真,並相應地優化程式碼。然而,無論優化設定如何,fatalError(_:file:line:)函式總是會停止執行。

您可以在原型和早期開發期間使用fatalError(_:file:line:)函式為尚未實現的功能建立存根,方法是編寫fatalError(“未實現”)作為存根實現。因為與斷言或先決條件不同,致命錯誤從來沒有被優化出來,所以可以肯定,如果遇到存根實現,執行總是會停止。

總結

通過這一章節的學習,可以瞭解Swift基礎的一些知識以及它特有的一些功能:

  • 如何去定義常量和變數
  • 如何新增註釋:和OC差不多,不過比OC多了個**"""**註釋多行的符號
  • 分號的作用:單行上寫多行程式碼時需要加上
  • 整型、浮點型和布林值
  • Swift特有的型別安全和型別推斷:這個真的很實用,可提高開發效率
  • 數字字面值和數值型別轉換
  • 型別別名
  • 元組
  • 可選值:?和!的用法,以及可選繫結if-let的用法
  • 錯誤處理:try-catch和do-try-catch
  • 斷言和先決條件 這裡就不詳細說明各個知識點了,可以從上面的內容中尋找答案。最後,如果大家喜歡的話,可以給個star哦,有你的支援,就是我的動力!

上一章節:開篇
下一章節:基本操作

參考檔案:Swift - The Basics