Swift 6 學習筆記(二)The Basics

這篇筆記也是同步 Swift 6 官方教程中的第二篇 《The Basics》,這篇博客中的大部分內容在第一篇中已經涉及,這篇可以被認為是基礎類型的的補充篇,多了很多說明信息。

  • 官方教學文檔 《The Basics》:

Swift 提供了許多基本數據類型,包括用于整數的 Int、用于浮點值的 Double、用于布爾值的 Bool 以及用于文本的 String。Swift 還提供了三種主要集合類型(數組、集合、字典)。

Swift 使用 變量 通過標識名稱來存儲和引用值,同時廣泛使用 值不可更改的變量 即常量,目的是讓代碼更安全、意圖更清晰。

除了常見的類型外,Swift 還引入了元組等高級類型。通過元組可以創建和傳遞一組值,使用元組將函數中的多個值作為單個復合值返回。

可選類型 用來處理值的缺失的情況,可選類型表示“存在一個值”或“根本沒有值”。可選值確保代碼在使用值之前始終檢查該值是否缺失,并且保證非可選值永遠不會缺失。

Swift 是一門 安全的語言,這意味著它有以下幾個特性:

  1. 可以在開發過程中盡早發現和修復幾類錯誤,并保證某些類型的錯誤不會發生;
  2. 能夠清楚地了解代碼所處理值的類型,如果部分代碼需要字符串,可以防止錯誤地將整數傳遞給它;
  3. 確保只能使用有效數據,而不是未初始化的內存或未初始化的對象;

Swift 在構建代碼時執行大部分安全檢查,并且在某些情況下會在代碼運行時執行額外的檢查。


1. Constants and Variables 常量與變量

常量和變量將一個名稱(例如 maximumNumberOfLoginAttemptsWelcomeMessage)與特定類型的值(例如數字 10 或字符串“Hello”)關聯起來。常量的值一旦設置就無法更改,而變量的值可以在將來設置為其他值。

1.1 Declaring Constants and Variables 常量與變量聲明

常量和變量必須在使用前聲明。使用 let 關鍵字聲明常量,使用 var 關鍵字聲明變量。以下示例展示了如何使用常量和變量來跟蹤用戶登錄嘗試次數:

let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0

這段代碼可以理解為:聲明一個名為 maximumNumberOfLoginAttempts 的新常量,并賦值為 10;聲明一個名為 currentLoginAttempt 的新變量,并賦值為 0

在此示例中,允許的最大登錄嘗試次數被聲明為常量,因為最大值永遠不會改變。當前登錄嘗試計數器被聲明為變量,因為該值必須在每次登錄嘗試失敗后遞增。

如果代碼中存儲的值不會改變,應始終使用 let 關鍵字將其聲明為常量。變量僅用于存儲會改變的值。
聲明常量或變量時,可以像上面的示例一樣在聲明過程中賦值,也可以在程序的后續階段賦值,只要確保在第一次使用之前它已經有一個值即可。

var enviroment = "development"
let maximumNumberOfLoginAttempts: Intif enviroment == "development"{maximumNumberOfLoginAttempts = 100
}else{maximumNumberOfLoginAttempts = 10
}

在此示例中,最大登錄嘗試次數是一個常量;在開發環境中其值為 100;在其他環境中其值為 10。if 語句的兩個分支都使用某個值初始化 maximumNumberOfLoginAttempts,從而確保該常量始終獲得一個值。

在同一行中聲明多個常量或變量,并用逗號分隔:

var x = 0.0, y = 0.0, z = 0.0

1.2 Type Annotations 類型注解

聲明常量或變量時可以提供類型注解,以明確該常量或變量可以存儲的值類型。編寫類型注解的方法是,在常量或變量名稱后添加一個冒號 :,然后跟一個空格,最后跟要使用的類型名稱。
此示例為名為welcomeMessage 的變量提供了一個類型注解,以指示該變量可以存儲字符串值:

var welcomeMessage: String

上面的代碼可以理解為:聲明一個名為 welcomeMessage 的變量,其類型為String。

welcomeMessage 變量設置為任何字符串值而不會出現錯誤:

var welcomeMessage: StringwelcomeMessage = "Hello"

可以在同一行上定義多個相同類型的相關變量,用逗號分隔,并在最終變量名后添加單個類型注釋:

var red, green, blue: Double

1.3 Naming Constants and Variables 變量與常量命名

常量和變量名可以包含幾乎任何字符,包括 Unicode 字符:

let π = 3.14159
let 你好 = "你好世界"
let 🐶🐮 = "dogcow"

但常量和變量名不能包含空格字符、數學符號、箭頭、私有的 Unicode 標量值或線條和框繪制字符。它們也不能以數字開頭,但數字可以出現在名稱的其他位置。

一旦聲明了某種類型的常量或變量,就不能再用相同的名稱聲明它,也不能將其更改為存儲不同類型的值。也不能將常量更改為變量,也不能將變量更改為常量。

可以將現有變量的值更改為兼容類型的其他值。在此示例中,friendlyWelcome 的值從“Hello!”更改為“Bonjour!”:

var friendlyWelcome = "Hello"
friendlyWelcome = "Bonjour"

與變量不同,常量的值一旦設置就無法更改。嘗試更改代碼時,編譯時會報錯:

let languageName = "Swift"
languageName = "Swift++"    // 報錯,常量不可修改

1.4 Printing Constants and Variables 輸出變量與常量

可以使用 print(_:separator:terminator:) 函數打印常量或變量的當前值:

var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"print(friendlyWelcome)		// Bonjour!

print(_:separator:terminator:) 函數是一個全局函數,它將一個或多個值打印到適當的輸出。例如,在 Xcode 中,print(_:separator:terminator:) 函數將其輸出打印在 Xcode 的“控制臺”窗格中。分隔符和終止符參數具有默認值,因此可以在調用此函數時省略它們。默認情況下,該函數會通過添加換行符來終止其打印的行。如果想要打印一個不帶換行符的值,需要傳遞一個空字符串作為終止符 - 例如,print(someValue, terminator: "")

var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"print(friendlyWelcome, terminator: "")

Swift 使用字符串插值,將常量或變量的名稱作為占位符包含在較長的字符串中,并提示 Swift 將其替換為該常量或變量的當前值。將名稱括在括號中,并在左括號前使用反斜杠進行轉義:

var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"print("The current value of friendlyWelcome is \(friendlyWelcome)")
// The current value of friendlyWelcome is Bonjour!

2. Comments 注釋

2.1 Single line comments 單行注釋

使用注釋在代碼中添加不可執行的文本,作為注釋或提醒,編譯器在編譯代碼時會忽略注釋。
Swift 中的注釋與 C 語言中的注釋非常相似。單行注釋以兩個正斜杠 // 開頭:

// This is a comment.

2.2 Multi line comments 多行注釋

多行注釋以斜杠加星號 /* 開頭,以星號加斜杠 */ 結尾:

/* This is also a comment
but is written over multiple lines. */

2.3 Multi line inside comments 整塊注釋

與 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. */

嵌套多行注釋能夠快速輕松地注釋掉大塊代碼,即使代碼已經包含多行注釋,這一點比 C++ 的邏輯方便太多了。


3. Semicolons 分號

Swift 并不要求在代碼中的每個語句后都強制添加分號 ; 即便加上也可以通過編譯。如果想在一行中編寫多個單獨的語句,則必須使用分號:

let cat = "🐱"; print(cat)
// 🐱

4. Integers 整型

整數是不帶小數部分的整數,例如 42 和 -23。整數可以是有符號整數(正數、零或負數),也可以是無符號整數(正數或零)。

Swift 提供 8、16、32 和 64 位的有符號整數和無符號整數。這些整數遵循與 C 語言類似的命名約定,即 8 位無符號整數的類型為 UInt8,32 位有符號整數的類型為 Int32。與 Swift 中的所有類型一樣,這些整數類型的名稱均采用大寫字母。

4.1 Integer Bounds 整型范圍

使用 minmax 屬性訪問每個整數類型的最小值和最大值:

let minValue = UInt8.min
let maxValue = UInt8.maxprint(minValue, maxValue)	// 0 255

這些屬性可以與相同類型的其他值一起在表達式中使用。

4.2 Int 整型

大多數情況下,無需在代碼中指定整數的大小。Swift 提供了一個額外的整數類型 Int,其大小 與當前平臺的原生字長相同

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

除非特殊情況,否則應該始終使用 Int 表示整數值,這樣有助于提高代碼的一致性和互操作性。即使在 32 位平臺上,Int 也可以存儲 ? 2 , 147 , 483 , 648 -2,147,483,648 ?2,147,483,648 2 , 147 , 483 , 647 2,147,483,647 2,147,483,647 之間的任何值,并且足以容納許多整數范圍。

4.3 UInt 無符號整型

Swift 還提供了一個無符號整數類型 UInt,其大小與當前平臺的原生字長相同:

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

5. Floating-Point Numbers 浮點型

浮點數是帶有小數部分的數字,例如 3.14159 3.14159 3.14159 0.1 0.1 0.1 ? 273.15 -273.15 ?273.15。浮點類型可以表示比整數類型更廣泛的值,并且可以存儲比 Int 類型更大或更小的數字。Swift 提供了兩種有符號浮點數類型:

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

6. Type Safety and Type Inference 類型安全與類型推斷

Swift 中的 每個值都有類型,可以使用類型注解明確指定類型,或者由 Swift 根據初始值推斷類型。每個提供值的地方,該值的類型都必須與使用它的地方匹配,如果代碼的某個部分需要一個字符串,就不能將其賦值一個整數,這種檢查使 Swift 成為一種類型安全的語言。

類型安全的語言要求明確代碼所處理值的類型,這樣一種類型的值永遠不會被隱式轉換為另一種類型。但是某些類型可以顯式轉換。在編譯時任何不匹配的類型標記為錯誤。

類型檢查并不意味著必須明確聲明的每個常量和變量的類型,如果未指定所需值的類型,Swift 會使用類型推斷來計算出合適的類型,類型推斷使編譯器能夠在編譯代碼時自動推斷特定表達式的類型。

由于類型推斷,Swift 所需的類型聲明比 C 或 Objective-C 等語言少得多,常量和變量仍然需要顯式指定類型

類型推斷通常是通過在聲明常量或變量時為其賦值(或字面量)來實現的:

let meaningOfLife = 42	// 編譯器推斷為 Int
let pi = 3.14159 		// 編譯器推斷為 Double		

Swift 在推斷浮點數的類型時始終選擇 Double 而不是 Float,如果在表達式中組合使用整數和浮點字面量,則會根據上下文推斷出 Double 類型:

let anotherPi = 3 + 0.14159		// 編譯器仍然會推斷為 Double

7. Numeric Literals 數字字面量

整數字面量可以寫成:

  • 十進制數,無前綴
  • 二進制數,前綴為 0b
  • 八進制數,前綴為 0o
  • 十六進制數,前綴為 0x

以下整數字面量都以十進制數 17 結尾:

let decimalInteger = 17			  // 17 in 十進制
let binaryInteger = 0b10001       // 17 in 二進制
let octalInteger = 0o21           // 17 in 八進制
let hexadecimalInteger = 0x11     // 17 in 十六進制

對于指數為 x x x 的十進制數,基數乘以 10 x 10^{x} 10x

  • 1.25e2 表示 1.25 x 102,即 125.0;
  • 1.25e-2 表示 1.25 x 10?2,即 0.0125;

對于指數為 x x x 的十六進制數,基數乘以 2 x 2^{x} 2x

  • 0xFp2 表示 15 x 22,即 60.0;
  • 0xFp-2 表示 15 x 2?2,即 3.75;

以下所有浮點字面值的十進制值均為 12.1875:

  • 0xFp2 表示 15 x 22, 即60.0;
  • 0xFp-2 表示 15 x 2?2,即 3.75;
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. Type Conversion 類型轉換

8.1 Numeric Type Conversion 數字類型轉換

代碼中所有通用的整數常量和變量都應使用 Int 類型,在日常情況下使用默認整數類型意味著整數常量和變量可以 立即在代碼中互操作,并且將與整數字面值的推斷類型匹配。

僅在當前明確需要時才使用其他整數類型,例如來自外部數據大小明確,或者出于性能、內存必要優化的考慮。在這些情況下使用明確的類型有助于捕獲意外的值溢出,并隱式地記錄所用數據的性質。

8.2 Integer Conversion 整型轉換

整數常量或變量可以存儲的數字范圍因類型而異。Int8 常量或變量可以存儲 -128 到 127 之間的數字,而 UInt8 常量或變量可以存儲 0 到 255 之間的數字。如果數字無法放入指定大小的整數類型的常量或變量中,則會在代碼編譯時報錯:

let cannotBeNegative: UInt8 = -1	// 報錯,不能承載負數
let tooBig: Int8 = Int8.max + 1		// 報錯,超過了范圍

由于每種數字類型可以存儲不同范圍的值,因此您必須根據具體情況選擇是否進行數字類型轉換。這種選擇方法可以避免隱藏的轉換錯誤,并有助于在代碼中明確類型轉換意圖。

要將一種特定的數字類型轉換為另一種,需要使用現有值初始化一個所需類型的新數字。在下面的示例中,常量 twoThousand 的類型為 UInt16,而常量 one 的類型為 UInt8,由于它們不是同一類型,因此不能直接將它們相加。

let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)

8.3 Integer and Floating-Point Conversion 整型與浮點轉換

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

let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine

浮點數到整數的轉換也必須顯式進行。整數類型可以用 DoubleFloat 值初始化:

let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNinelet integerPi = Int(pi)

以這種方式初始化新的整數值時,浮點值會被??截斷,如 4.75 會被截斷為 4,-3.9 會被截斷為 -3。


9. Type Aliases 類型別名

類型別名可以為現有類型定義一個替代名稱,使用 typealias 關鍵字定義類型別名。

typealias AudioSample = UInt16

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

typealias AudioSample = UInt16
var maxAmplitudeFound = AudioSample.minprint(maxAmplitudeFound)    // 0

10. Booleans 布爾值

布爾值被稱為邏輯值只能為真或假:truefalse

let orangesAreOrange = true
let turnipsAreDelicious = false

orangesAreOrangeturnipsAreDelicious 的類型已被推斷為 Bool,與上面的 IntDouble 一樣,如果常量或變量在創建時就設置為 true 或 false,則無需將其聲明為 Bool。

用 Bool 類型控制條件語句:

let turnipsAreDelicious = falseif turnipsAreDelicious {print("Mmm, tasty turnips!")
} else {print("Eww, turnips are horrible.")
}

Swift 的類型安全機制阻止將非布爾值替換為 Bool 值。以下示例會報告編譯時錯誤:

let var = 1
if var{// ... 編譯報錯
}

但是判等條件是可以用的:

let var = 1
if var == 1{// ... 編譯通過
}

11. Tuples 元組

元組將多個值組合成一個復合值。元組中的值可以是任意類型,并且彼此的類型不必相同。

下面的代碼中 (404, "Not Found") 是一個描述 HTTP 狀態代碼的元組,如果您請求的網頁不存在,則會返回狀態代碼 404 Not Found,這個元組將一個 Int 和一個 String 組合在一起。

let http404Error = (404, "Not Found")

可以將元組的內容分解為單獨的常量或變量并進行訪問:

let http404Error = (404, "Not Found")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 http404Error = (404, "Not Found")let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")    // The status code is 404

也可以使用從零開始的索引號訪問元組中的各個元素值:

let http404Error = (404, "Not Found")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 http404Error = (statusCode: 200, description: "OK")print("The status code is \(http404Error.statusCode)")      // The status code is 200
print("The status message is \(http404Error.description)")  // The status message is OK

12. Optionals 可選類型

在值可能缺失的情況下,可以使用可選類型 Optionals。可選類型代表兩種可能性:要么存在指定類型的值,要么根本沒有值。

以下示例使用初始化器嘗試將字符串轉換為 Int:

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
print(type(of: convertedNumber))        // Optional<Int>

12.1 nil 空值

可以通過為可選變量分配特殊值 nil 來將其設置為無值狀態:

var serverResponseCode: Int? = 404
serverResponseCode = nil
print(serverResponseCode == nil)        // true

如果定義可選變量而不提供默認值,則該變量將自動設置為 nil:

var suveryAnswer: String?
print(suveryAnswer == nil)  // true

可以使用 if 語句通過將可選值與 nil 進行比較來判斷其是否包含值。如果可選值有值,則 != nil

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)if convertedNumber != nil{print("convertedNumber contains some integer value.")
}
// convertedNumber contains some integer value.

不能將 nil 與非可選常量或變量一起使用。如果代碼中的常量或變量需要在某些條件下處理值缺失的情況,應將其聲明為相應類型的可選值。聲明為非可選值的常量或變量則需要確保永遠不會等于 nil 值,否則會引發編譯時錯誤。

這種可選值和非可選值的分離可以 明確標記哪些信息可以缺失,并使編寫處理缺失值的代碼。解包值后,其他使用該值的代碼都無需檢查其是否為 nil,因此無需在代碼的不同部分重復檢查相同的值。

訪問可選值時,代碼始終會同時處理 nil 和非 nil 的情況。當值缺失時,可以執行以下幾項操作:

  • 當值為 nil 時,跳過對值進行操作的代碼;
  • 通過返回 nil 或使用可選鏈式調用中描述的 ?. 運算符來傳播 nil 值;
  • 使用 ?? 運算符提供回退值;
  • 使用 ! 運算符停止程序執行。

12.2 Optional Binding 可選邦定

可以使用可選綁定來判斷可選值是否包含值,如果包含則將該值用作臨時常量或變量。可選綁定可以與 ifguardwhile 語句一起使用,用于檢查可選值中的值,并將該值提取到常量或變量中,作為單個操作的一部分。

if 語句編寫一個可選綁定,下面的代碼意思為:如果 Int(possibleNumber) 返回的可選 Int 類型包含值,則將名為 actualNumber 的新常量設置為該可選類型包含的值。

let possibleNumber = "123"if let actualNumber = Int(possibleNumber){print("The string \"\(possibleNumber)\" has an integer value of \(actualNumber)")
}else{print("The string \"\(possibleNumber)\" couldn't be converted to an integer")
}
// The string "123" has an integer value of 123

如果在訪問其包含的值后不需要引用原始的可選常量或變量,則可以對 新的常量或變量使用相同的名稱,這段代碼首先檢查 myNumber 是否包含值,如果 myNumber 有值,則將名為 myNumber 的新常量的值設置為該值。

let possibleNumber = "123"
let myNumber = Int(possibleNumber)if let myNumber = myNumber{print("My number is \(myNumber)")
}
// My number is 123

可以將常量和變量與可選綁定一起使用。如果在 if 語句的第一個分支中操作 myNumber 的值,可以改寫為 if var myNumber ,這樣可選值中包含的值將作為變量而不是常量使用。在 if 語句主體中對 myNumber 所做的更改僅適用于該局部變量,而不會影響解包的原始可選常量或變量。

在單個 if 語句中包含 任意數量 的可選綁定和布爾條件并以逗號分隔。如果可選綁定中的任何值為 nil 或任何布爾條件的計算結果為 false,則整個 if 語句的條件將被視為 false:

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

12.3 Providing a Fallback Value 提供備選值

處理缺失值的另一種方法是使用空值合并運算符 ?? 提供默認值。如果 ?? 左側的可選項不為 nil,則解包并使用該值;否則使用 ?? 右側的值。下面的代碼會在指定姓名的情況下使用姓名問候某人,當姓名為 nil 時,使用通用問候語。

var name: String? = nil
let greeting = "Hello, " + (name ?? "friend") + "!"print(greeting) // Hello, friend!

13. Unwrapping 解包

13.1 Force Unwrapping 強制解包

nil 表示不可恢復的故障時,可以通過在可選項名稱末尾添加感嘆號 ! 來訪問底層值,稱為強制解包可選項的值。強制解包非 nil 值時,返回其解包后的值;強制解包 nil 值會觸發運行時錯誤。

! 實際上是 fatalError(_:file:line:) 的縮寫,以下代碼展示了兩種等效的方法:

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)let number = convertedNumber!guard let number = convertedNumber else{fatalError("The number was invalid")
}

13.2 Implicitly Unwrapped Optionals 隱式解包可選值

可選值表示常量或變量可以“無值”。可以使用 if 語句檢查可選值是否存在,如果存在,可以使用可選綁定進行條件解包以訪問可選值。可選值在首次設置后始終會有一個值,在這種情況下無需在每次訪問可選值時都檢查并解包它,因為可以安全地假定始終有值。

這類可選值被定義為 隱式解包可選值,編寫隱式解包可選值時需要在可選值的類型后放置感嘆號 String! 而不是問號 String?。使用可選值時,應在聲明可選值時在其類型后放置 !,而不是在其名稱后放置 !

Swift 中隱式解包可選項的主要用途是在 類初始化期間。如果變量以后有可能變為 nil,請勿使用隱式解包可選類型。如果需要在變量的生命周期內檢查其是否為 nil 值,應始終使用普通可選類型。
隱式解包可選類型在后臺與普通可選類型相同,但也可以像非可選值一樣使用,無需在每次訪問時都解包可選值。以下示例展示了在以顯式字符串形式訪問可選字符串和隱式解包可選字符串的包裝值時,兩者的行為差異:

let possibleString: String? = "An optional string"
let forcedString: String = possibleString!      // 顯示解包let assumedString: String! = "An implicitly unwrapped optional string"
let implicitString: String = assumedString      // 隱式解包

可以將隱式解包的可選值理解為 允許在需要時強制解包該可選值。當使用隱式解包的可選值時,Swift 首先會嘗試將其用作普通的可選值;如果不能被用作可選值,Swift 會強制解包該值。在上面的代碼中,可選值 supposedString 在賦值給 implicitString 之前被強制解包,因為 implicitString 具有顯式的非可選類型 String

在下面的代碼中,optionalString 沒有顯式類型,因此它是一個普通的可選值:

let assumedString: String! = "An implicitly unwrapped optional string"let optionalString = assumedString

如果隱式解包的可選類型為 nil,當嘗試訪問其包裝值會觸發 runtime error。其結果與使用 ! 強制解包一個不包含值的普通可選類型完全相同。可以像檢查普通可選類型一樣檢查隱式解包的可選類型是否為 nil

let assumedString: String! = "An implicitly unwrapped optional string"if assumedString != nil{print(assumedString!)
}
// An implicitly unwrapped optional string

還可以使用帶有可選綁定的隱式解包可選值,在單個語句中檢查并解包其值:

let assumedString: String! = "An implicitly unwrapped optional string"if let definiteString = assumedString{print(definiteString)
}
// An implicitly unwrapped optional string

14. Memory Safety 內存安全

除了上面類型安全和類型推斷中描述的防止類型不匹配的檢查之外,Swift 還保護代碼免受無效內存的影響。這種保護稱為內存安全,并包含以下要求:

  • 值在讀取之前設置。防止與未初始化內存區域交互的保護也稱為明確初始化。
  • 數組和緩沖區只能在有效索引處訪問。防止越界訪問的保護也稱為邊界安全。
  • 內存只能在值的生命周期內訪問。防止釋放后使用錯誤的保護也稱為生命周期安全。
  • 內存訪問僅以可證明安全的方式重疊。防止并發代碼中可能出現的數據競爭的保護也稱為線程安全。

如果你過去使用過類型不安全語言(如C++),可能熟悉上面列出的一些錯誤和 bug。Swift 中的安全代碼可以避免這些問題。

有時需要在安全范圍之外做些工作,例如由于語言或標準庫的限制,Swift 也提供了某些 API 的非安全版本。當使用名稱中包含“不安全”、“未檢查”或“非托管”等字眼的類型或方法時,需要自己承擔安全責任。

盡管 Swift 中的安全代碼,但仍然可能遇到錯誤和意外故障導致程序停止執行。Swift 提供了幾種指示和恢復錯誤的方法,但在某些情況下,處理錯誤的唯一安全方法是停止執行。如果需要保證服務永遠不會意外停止,請在其整體架構中加入容錯功能,以便它可以從任何組件的意外停止中恢復。


15. Error Handling 異常處理

可以使用錯誤處理來響應程序在執行過程中可能遇到的錯誤情況。

與可選值不同,錯誤處理用來定位失敗的根本原因,并在必要時將錯誤傳播到程序的其他部分。當函數遇到錯誤情況時會拋出一個錯誤。該函數的調用者可以捕獲該錯誤并做出適當的響應。

func canThrowAnError() throws{// 在函數中可能拋出錯誤
}

函數聲明中包含 throws 關鍵字表示可以拋出錯誤。調用一個可以拋出錯誤的函數時,需要在表達式前面添加 try 關鍵字。Swift 會自動將錯誤傳遞到當前作用域之外,直到被 catch 子句處理。

func canThrowAnError() throws{// 在函數中可能拋出錯誤
}do {try canThrowAnError()// 沒有異常拋出時運行到這個位置
}catch{// 拋出異常后跳轉到這里
}

do 語句會創建一個新的包含作用域,允許將錯誤傳播到一個或多個 catch 子句。以下示例展示了如何使用錯誤處理來響應不同的錯誤條件:

enum SandwichError: Error{case outOfCleanDishescase missingIngredients(ingredients:Int)
}func makeASandwich() throws{// ...
}func eatASandwich(){// ...
}func washDishes(){// ...
}func buyGroceries(ingredients: Int){// ...
}do {try makeASandwich()eatASandwich()
}catch SandwichError.outOfCleanDishes {washDishes()
}catch SandwichError.missingIngredients (let ingredients){buyGroceries(ingredients: ingredients)
}

16. Assertions and Preconditions 斷言與先決條件

AssertionsPreconditions 是在運行時進行的檢查,可以使用它們來確保在執行任何后續代碼之前滿足基本條件。如果斷言或先決條件中的布爾條件為真,則代碼將繼續照常執行;如果條件為假,則程序的當前狀態無效。

可以使用斷言和先決條件來表達在編碼時所做的假設和期望,斷言可以在開發過程中發現錯誤和不正確的假設先決條件可以在生產環境中檢測問題。

除了在運行時驗證期望之外,斷言和先決條件也是代碼中一種有用的文檔形式。與上面“錯誤處理”中討論的錯誤條件不同,斷言和先決條件不用于可恢復或預期的錯誤。由于失敗的斷言或先決條件表示程序狀態無效,因此無法捕獲失敗的斷言。當斷言失敗時,程序中至少有一部分數據無效,但并無法知道無效的原因,也不知道其他狀態是否也無效。

使用斷言和前提條件并不能替代以不太可能出現無效條件的方式設計代碼,但使用它們來強制執行有效的數據和狀態,可以用在出現無效狀態時進行可預測地終止,并有助于更輕松地調試問題。如果不檢查假設,可能要等到很久以后當其他代碼部分開始明顯出現故障,并且用戶數據已悄無聲息地損壞時,才會注意到此類問題。一旦檢測到無效狀態,立即停止執行也有助于限制該無效狀態造成的損害。

斷言和先決條件的區別在于檢查的時間:斷言僅在調試版本中檢查而先決條件在調試版本和生產版本中都會檢查。在生產版本中,斷言中的條件不會被評估,可以在開發過程中使用任意數量的斷言,而不會影響生產性能。

16.1 Debugging with Assertions 使用斷言Debug

可以通過調用 Swift 標準庫中的 assert(_:_:file:line:) 函數來編寫斷言,需要向此函數傳遞一個計算結果為 true 或 false 的表達式,以及一條在條件結果為 false 時顯示的消息。例如:

let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// 觸發斷言,因為條件不滿足

可以省略斷言消息:

let age = -3
assert(age >= 0)

如果代碼已經檢查了條件,則可以使用 assertionFailure() 函數來直接觸發斷言失敗:

let age = -3if 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.")
}

16.2 Enforcing Preconditions 執行先決條件

當條件有可能為假,但必須為真才能使代碼繼續執行時,可以使用 precondition,如使用前提條件檢查下標是否超出范圍,或檢查函數是否已傳遞有效值。

可以通過調用 precondition() 函數來編寫前提條件,需要向此函數傳遞一個計算結果為真或假的表達式,以及一條在條件結果為假時顯示的消息。例如:

let index = 0
precondition(index > 0, "Index muet be greater than zero")

可以調用 preconditionFailure() 函數來指示發生了失敗,例如 switch 的默認情況,但所有有效輸入數據都應該由 switch 的其他情況之一處理。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/83489.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/83489.shtml
英文地址,請注明出處:http://en.pswp.cn/web/83489.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【PHP】BC Math 函數參考表

BC Math 函數參考表: 函數名描述語法bcadd兩個任意精度數字的加法bcadd($num1, $num2, [scale])bcsub兩個任意精度數字的減法bcsub($num1, $num2, [scale])bcmul兩個任意精度數字乘法bcmul($num1, $num2, [scale])bcdiv兩個任意精度數字除法bcdiv($num1, $num2, [scale])bcmod…

C# TAP異步編程(Task/async/await)總結

C#中有個很好用的東西&#xff0c;TAP異步編程&#xff08;Task-based Asynchronous Pattern&#xff09;&#xff0c;是目前C#推薦的異步編程模型。它基于 System.Threading.Tasks.Task 和 async/await 關鍵字&#xff0c;旨在簡化異步代碼的編寫、調試和維護。TAP 是現代 .NE…

達夢數據庫(DM)用戶名大小寫處理規則

達夢數據庫(DM)用戶名大小寫處理規則 達夢數據庫對用戶名的處理與PostgreSQL和Oracle有所不同&#xff0c;以下是相關說明&#xff1a; 一、基本規則 默認情況下&#xff1a;達夢數據庫區分用戶名大小寫 創建的用戶名會保留原始大小寫格式連接時必須使用相同的大小寫形式 …

黑馬點評面試話術

文章目錄 1.項目介紹2. 分布式登錄功能2.1 講講登錄的整個流程2.2 集群模式session下存儲用戶信息會有啥問題&#xff1f;2.3 為什么采用redis存儲用戶信息和驗證碼2.4 redis的存儲格式怎么樣的&#xff1f;2.5 為什么采用Hash結構存儲用戶信息2.6 為什么采用雙攔截器&#xff…

MTK APEX測光系統中各變量具體的計算方式探究

目錄 一、APEX測光系統介紹 二、MTK測光系統實例介紹 三、關于測光系統的一些疑問 一、APEX測光系統介紹 詳細內容可以參考; AE(自動曝光)系統簡介

K8S的基本概念

Kubernetes是一個開源的容器編排部署管理平臺,用于管理云平臺中多個主機上的容器化應用。Kubernetes的目標是讓部署容器化的應用簡單并且高效,Kubernetes提供了應用部署、規劃、更新、維護的一種機制。 對應用開發者而言,可以把Kubernetes看成一個集群操作系統。Kubernetes…

NLP學習路線圖(三十四): 命名實體識別(NER)

一、命名實體識別(NER)是什么? 命名實體識別(Named Entity Recognition, NER)是自然語言處理中的一項關鍵序列標注任務。其核心目標是從非結構化的文本中自動識別出特定類別的名詞性短語,并將其歸類到預定義的類別中。 核心目標:找到文本中提到的命名實體,并分類。 典…

大三自學筆記:探索Hyperlane框架的心路歷程

## Day 1&#xff1a;初識 Hyperlane 在 GitHub 上發現了 Hyperlane 這個 Rust HTTP 框架&#xff0c;立刻被它的性能數據吸引。官方文檔寫著&#xff1a; > "hyperlane 是一個高性能且輕量級的 Rust HTTP 框架&#xff0c;設計目標是簡化現代 Web 服務的開發&#xff…

Java大廠面試真題:謝飛機的技術挑戰

Java大廠面試真題&#xff1a;謝飛機的技術挑戰 場景一&#xff1a;電商場景 面試官&#xff1a;在電商項目中&#xff0c;我們通常需要處理大量的并發請求。請談談你對JVM調優的理解。 謝飛機&#xff1a;嗯&#xff0c;JVM調優主要是為了提高程序的性能和穩定性。比如&…

【Docker管理工具】安裝容器管理工具Oxker

【Docker管理工具】安裝Oxker容器管理工具 一、Oxker介紹1.1 Oxker簡介1.2 Oxker功能1.3 Docker介紹 二、本地環境介紹2.1 本地環境規劃2.2 本次實踐介紹 三、本地環境檢查3.1 檢查Docker服務狀態3.2 檢查Docker版本3.3 檢查docker compose 版本 四、下載Oxker鏡像五、安裝Oxke…

產品成本分析怎么做?從0到1搭建全生命周期分析框架!

目錄 一、為什么要做產品全生命周期成本分析&#xff1f; 1.資源再分配 2.動態成本校準 3.戰略決策支持 二、產品成本分析思路 1.建立全生命周期成本追蹤 2.聯動分析關鍵指標 3.定位問題產品線 4.資源效率四象限分配 三、產品成本分析指標 1.分投入成本&#xff1a;…

機器學習與深度學習20-數學優化

目錄 前文回顧1.梯度下降的基本原理2.什么是損失函數&#xff1f;3.隨機梯度下降和小批量梯度下降4.什么是學習率5.優化算法中的收斂性6.常用的數學優化算法 前文回顧 上一篇文章鏈接&#xff1a;地址 1.梯度下降的基本原理 梯度下降&#xff08;Gradient Descent&#xff0…

Photoshop 2025 性能配置全攻略:硬件選購與軟件優化指南

一、硬件配置核心建議 根據Adobe官方要求及實測反饋&#xff0c;Photoshop 2025對硬件的需求側重CPU、內存和存儲&#xff0c;顯卡需求相對寬松&#xff0c;但特定功能&#xff08;如AI濾鏡、3D渲染&#xff09;需關注顯卡性能。 硬件類別最低配置推薦配置&#xff08;流暢運…

華為云Flexus+DeepSeek征文 | 華為云ModelArts Studio快速上手:DeepSeek-R1-0528商用服務的開通與使用

華為云FlexusDeepSeek征文 | 華為云ModelArts Studio快速上手&#xff1a;DeepSeek-R1-0528商用服務的開通與使用 引言一、ModelArts Studio平臺介紹華為云ModelArts Studio簡介ModelArts Studio主要特點 二、開通DeepSeek-R1-0528商用服務訪問ModelArts Studio控制臺DeepSeek-…

day53 神經網絡調參指南

目錄 一、引言 二、權重初始化&#xff1a;為何如此重要&#xff1f; &#xff08;一&#xff09;隨機種子&#xff1a;確保實驗的可重復性 &#xff08;二&#xff09;權重初始化的重要性 1. 神經網絡的對稱性問題 2. 避免梯度消失和梯度爆炸 &#xff08;三&#xff0…

【大模型02---Megatron-LM】

文章目錄 Megatron-LM數據并行模型并行張量并行流水線并行 3D并行 Megatron-LM Megatron是當前大模型訓練時經常使用的一種分布式并行框架&#xff0c;它通過采用DP,TP,PP等來加速模型的訓練&#xff0c;反正就是一個字&#xff0c;好。 大模型在訓練的時候&#xff0c;顯存占…

魔百和網絡機頂盒CM211-1硬件解析

先來個正面照 背面照 核芯 無線網卡 支持WiFi與藍牙 硬盤 正面內存與背面內存

Kratos 與Golang Cms的關系

Kratos 與 Golang CMS 的關系 Kratos 是 Bilibili 開源的一款輕量級 Go 語言微服務框架,專注于構建高性能、可擴展的后端服務。雖然它本身并不是一個完整的 CMS(內容管理系統),但它可以用于開發 CMS 系統的后端或 API 服務。 我們的目標是提供全面的微服務開發技術。基于…

在vue3+vite中給 Video視頻 添加字幕

Video視頻 添加字幕 方式一: 使用 track標簽template標簽中css樣式修改方式二:直接讀取.vtt文件方式一: 使用 track標簽 參考1:https://blog.csdn.net/weixin_42321819/article/details/112442773 參考2:https://blog.csdn.net/foren_whb/article/details/80810552 template標…

UE4手動實現billboard效果讓物體始終面向相機正面

一個很簡單的需求&#xff0c;但在網上竟然沒查到。首先不能用FindLookAtRotation&#xff0c;因為這是用location算的&#xff0c;是讓物體朝向相機的方向&#xff0c;而不是朝向相機的正面。區別如下圖所示&#xff1a; 然后想用billboard component&#xff0c;不過這個原生…