### Swift 可選型
可選類型用來表示值缺失的情況。我們可以在具體的值與 `nil` 之間做一個選擇。
### 可選類型定義
```
var x : Int?
var y : Int
y = 10
print("\(x) -- \(y)") // 輸出:nil -- 10
```
> `Int?` 與 `Int` 是兩種完全不同的數據類型,前者為整型的可選行,后者是整型。
如果我們不給 `y` 賦值的話,程序將拋出錯誤。如下圖:

因為如果聲明了一個可選變量或者常量而沒有賦值,則默認為 `nil` ,`nil` 不能用于非可選的常量或者變量。
其次,聲明了一個非可選型的常量或者變量并沒有賦值的之前,當我們輸出的時候會拋出異常。
### 可選型的取值
#### 使用`!`進行強制解包
當確定可選類型確實包含值之后,可以在可選的名字后?加?個感嘆號( `!` )來獲取值。這個感嘆號表?“我知道這個可選有值,請使?它。”這被稱為可選值的強制解析。
```
"The errorCode is " + errorCode! // The errorCode is 404
```
> **注意:** 這種寫法是在明確知道了可選型不等于 `nil` 的情況下使用。如果可選型的值為 `nil` 則會拋出 **fatal error** 致命錯誤。
#### 判斷不是 `nil`
解包前對數據進行判斷,如果不等于 `nil` 則進行解包操作。
```
var errorCode: String? = "404"
if errorCode != nil {
"The errorCode is " + errorCode!
}else{
"No Error"
}
```
#### `if-let` 解包
還可以給解包的值使用 `if let` 語句進行賦值。
```
var errorCode: String? = "404"
if let upwrappedErrorCode = errorCode { // 允許解包的常量名和可選型的名字一致
"The errorCode is " + upwrappedErrorCode
}else{
"No Error"
}
```
> 當然也可以使用 `if var` 的方式對可選型進行解包,不過通常在大多數的情況下,我們對一個可選性只是對其解包讀取值,并不會去修改可選型的值。
#### 可以使用相同的變量名
也可以將上面的常量 `upwrappedErrorCode` 允許使用和 `errorCode` 一致的名稱。
```
var errorCode: String? = "404"
if let errorCode = errorCode {
"The errorCode is " + errorCode
}else{
"No Error"
}
```
> 這種解包方式只能在 `{}` 中訪問 `errorCode` 常量。在其他地方訪問 `errorCode`變量依然是可選型。
#### 使用 `if-let` 同時解包多個變量
如果存在兩種或者多種需要解包判斷的情況,我們可以使用更加優雅的寫法
```
var errorCode: String? = "404"
var errorMessage: String? = "Not Found"
if let errorCode = errorCode ,
let errorMessage = errorMessage {
"The errorCode is " + errorCode + "\nThe errorMessage is " + errorMessage
}
// 以上寫法等同于
if let errorCode = errorCode {
if let errorMessage = errorMessage {
"The errorCode is " + errorCode + "\nThe errorMessage is " + errorMessage
}
}
```
還可以新增一些判斷條件,如下:
解包兩個可選型,并判斷 `errorCode` 的值是否等于 404 。
```
if let errorCode = errorCode ,
let errorMessage = errorMessage , errorCode == "404" {
"page not found"
}
```
#### 可選型 Chaining
```
var errorMessage: String? = "Not Found"
errorMessage?.uppercased()
```
雖然 `errorMessage` 是一個可選型,這里嘗試對它進行解包,如果解包成功(即不等于 `nil`)那么對他進行 `uppercased()` 操作;如果解包失敗則不回執行 `uppercased()` 操作。
如果可選型 `errorMessage` 等于 `nil` ,則會返回 `nil`。
邏輯等同于如下寫法
```
var errorMessage: String? = "Not Found"
if let errorMessage = errorMessage {
errorMessage.uppercased()
}
```
另一種寫法
```
errorMessage!.uppercased()
```
將可選型 `errorMessage` 進行強制解包,并調用 `uppercased()` 操作,如果可選型 `errorMessage` 等于 `nil` 將會拋出 **fatel error** 致命錯誤,這種寫法一般情況下是不安全的,也是不推薦的。
```
var uppercaseErrorMessage = errorMessage?.uppercased() // 變量 `uppercaseErrorMessage` 是一個可選型
```
```
if let errorMessage = errorMessage?.uppercased() {
errorMessage
}
```
> 上面的寫法將解包和調用操作以及賦值基于一體,使邏輯更加清晰,減少代碼的出錯率。
#### 可選型 `Nil-Coalesce`
如下需求: `message` 變量的值待定,如果 `errorMessage` 可選型的值不等于 `nil` 時,變量 `message` 的值將等于 `errorMessage` 可選型解包后的值,否則將被賦值為 `no error`
```
var errorMessage: String? = nil
let message: String
if let errorMessage = errorMessage {
message = errorMessage
}else{
message = "no error"
}
```
我們可以使用三元運算符進行改寫(返回可選型解包后的值)
```
let message2: String = ( errorMessage != nil ) ? errorMessage! : "no error"
```
也可以使用 Swift 更簡潔的方法(返回可選型解包后的值)
```
let message3 = errorMessage ?? "no error"
```
### 可選型在元組中的使用
#### 將元組中的某個單元設置為可選型
```
var error: ( errorCode: Int , errorMessage: String? ) = ( 404 , "Not Found" )
print(error) // (404, Optional("Not Found"))
error.errorMessage = nil // 將元組的第一個分量設置為 `nil`
print(error) // (404, nil)
```
#### 將整個元組設置為可選型
```
var error2: ( errorCode: Int , errorMessage: String )? = ( 404 , "Not Found" )
// error2.errorMessage = nil // 這樣設置會報錯
error2 = nil // 這樣操作是允許的
```
#### 將元組中的某個值以及整個元組都設置為可選型
```
var error3: ( errorCode: Int , errorMessage: String? )? = ( 404 , "Not Found" )
```
### 可選型的一些實際應用
#### 獲取用戶輸入的年齡
```
var ageInput: String = "19"
var age = Int( ageInput ) // 將用戶輸入強制轉換成整型
if let age = age , age < 20 {
print("You're a teenager!")
}
```
#### 字符串字串范圍
```
var str = "Hello"
str.range(of: "o") // 4..<5
str.range(of: "is") // nil
```
### 隱式可選型
隱式可選性使用`!`來聲明,相比于可選型的不同是,這種類型的變量或者常量**可以不使用解包就讀取變量或者常量的值而直接使用。** 基本用于類的創建時屬性的定義。
```
var errorMessage: String! = nil
errorMessage = "Not Found"
"The message is " + errorMessage
```
> 這種可選型在使用的時候可以不解包,直接使用,這種隱式可選性是不安全的,因為我們不需要解包就可以使用,當可選性的值是nil的時候講會給程序帶來error
```
class City {
let cityName: String
unowned var country: Country
init(cityName: String, country: Country) {
self.cityName = cityName
self.country = country
}
}
class Country {
let countryName: String
var capitalCity: City! // 隱式可選性
init(countryName: String, capitalCity: String) {
self.countryName = countryName
self.capitalCity = City(cityName: capitalCity, country: self)
}
func showInfo() {
print("This is \(countryName)")
print("The capital is \(capitalCity.cityName)")
}
}
let china = Country(countryName: "中國", capitalCity: "北京")
china.showInfo()
```
- 學習筆記
- 基礎
- 基本類型之整型
- 基本類型之浮點型
- 基本類型之布爾類型以及簡單的 if 語句
- 基礎類型之元組
- 基本類型之其他
- 運算符
- 基礎運算符
- 比較運算符、邏輯運算符
- 三元運算符
- 范圍運算符for-in
- 邏輯控制
- 循環結構
- 選擇結構
- 字符串
- Character和Unicode
- String.index 和 range
- 可選型
- 容器類
- 數組初始化
- 數組基本操作
- 字典初始化
- 字典基本操作
- 集合初始化
- 集合基本操作
- 函數
- 閉包
- 枚舉
- 結構體
- 類
- 文檔注釋
- 屬性和方法
- 下標和運算符重載
- 拓展和泛型
- 協議
- 其他
- Swift 3.0 For 循環
- Swift 隨機數的生成
- IOS開發玩轉界面 UIKit
- UILable 文本顯示控件
- UIButton 簡單的交互控件
- UIImageView 圖片控件
- UISearchBar 搜索控件