在值可能不存在的情況下使用可選值(optional), 可選值是:
· 存在一個值,這個值等于 x
或
· 不存在任何值
注:
在 C 和 Objective-C 中可選值的概念并不存在。Objective-C 中(與可選值)最相近的(概念)是,一個以對象為返回值的方法,也可以返回 nil,nil表示“不存在有效的對象”。不過,這規則只對對象有效——對于結構、基本的 C 類型或枚舉值無效。對于這些類型,Objective-C 語言的方法通常會返回一個特殊值(如
NSNotFound
)來表示值不存在。這種策略假定該方法的調用方知道要測試返回值是否等于某個特殊值,并且記得要作此檢查。Swift 的可選值允許表示任何類型不存在值,無需定義特殊常量。
這里有個例子。Swift 的 String
類型有一個名為 toInt
的方法,可嘗試將 String
值轉為 Int
值。然而,不是所有字符串都可以轉換為整數。字符串 “123”
可以轉換為數值 123
,而字符串 “hello, world”
顯然沒有對應的數值。
下面的例子會嘗試用 toInt
方法將 String
轉換為 Int
:
1 let possibleNumber(可能是數字) = "123"2 let convertedNumber(轉換得到的數字) = possibleNumber.toInt()
3 //convertedNumber被推斷為 "Int?" 類型,或 "可選的 Int"
因為 toInt
方法可能會轉換失敗,因此它返回的是一個 可選的 Int
型,而不是 Int
型。可選的 Int
寫法為 Int?
,而不是 Int
。問號表明示該類型包含的值是可選的,即 Int?
可能包含某個 Int
類型的值,也可能不含任何值。(它不能包含其他類型的值,如 Bool
值或 String
值,它要么是 Int 要么就
不存在。)
if
語句與強制拆包可使用 if
語句測試可選值是否含有值。如果有,則求值結果返回 true
,否則返回 false
。
一旦確認可選值的確含有一個值,就可以通過在可選值變量名的末尾添加感嘆號(!
)來訪問其內部的值。感嘆號明確表達:“我知道這個可選值確實存在值,請使用它(那個值)吧!”這種操作名為對可選值進行強制拆包(force-unwrap):
1 if convertedNumber(轉換后得到的數字) {2 PRintln("/(possibleNumber) 的整數值為 /(convertedNumber!)")3 } else {4 println("/(possibleNumber) 不能轉換為一個整數")5 }6 // 輸出 "123 的整數值為 123"
關于 if
語句的更多信息,請見 流程控制。
注:
嘗試用
!
訪問不存在的可選值時會在運行時報錯,在用!
強行拆包之前,務必確保可選值確實包含一個非nil
的值。
可以使用可選值綁定(optional binding)測試可選值是否包含一個值,如果有,則將該值以臨時常量或變量的形式拆包備用。可選值綁定可以與 if
或 while
語句結合使用,這樣只需要一步就可以檢查是否存在值、提取該值、并存放到常量或變量中。關于 if
與 while
語句的更多信息在 流程控制 一章中有講過。
可選值綁定與 if 混用可以是這樣:
1 if let constantName = someOptional {2 statements3 }
上文中的 possibleNumber 一例,可以用可選值綁定代替強制拆包:
1 if let actualNumber = possibleNumber.toInt() {2 println("/(possibleNumber) 的整數值為 /(actualNumber)") 3 } else { 4 println("/(possibleNumber) 無法轉換為整數") 5 } 6 // 輸出 "123 的整數值為 123"
可以這樣理解:
“如果 possibleNumber.toInt()
返回的 可選的 Int
包含一個值,則新建一個名為 actualNumber
的常量,并將常量的值設為可選值中包含的值。”
如果轉換成功,常量 actualNumber
將可供 if
語句的第一個分支語句使用。該常量已經被可選值包含的值初始化,因此不再需要用后綴 !
訪問其值。本例中,actualNumber
被直接用來輸出轉換結果。
常量與變量均可用于可選值綁定。如果需要在第一個分支中處理 actualNumber 的值,可以改寫為 if var actualNumber
,這樣可選值的值將作為變量而非常量拆包。
要將可選變量設為空值(沒有值或值不存在的狀態,valueless),可以給它賦特殊值 nil
:
1 var serverResponseCode(服務器狀態碼): Int? = 4042 //服務器狀態碼包含一個實際存在的 Int 值:404
3 serverResponseCode= nil
4 // 服務器狀態碼 現在不含任何值
注:
nil
不能用于非可選值的常量或變量。如果代碼中的常量或變量需要適配值不存在的某種特殊情況,務必將它聲明為適用的可選值類型。
如果你所定義的可選值未提供默認值,該常量或變量將被自動設為 nil
:
1 var surveyAnswer: String?2 // surveyAnswer 被自動設為 nil
注:
Swift 的
nil
與 Objective-C 的nil
不同。Objective-C 的nil
是指向一個不存在對象的指針,而 Swift 的nil
不是指針——它是一種表示值不存在的特殊類型。任何類型的可選值都能賦值為nil
,不僅限于對象類型。
如上所述,可選值指允許“值不存在”的常量或變量。可以通過 if
語句測試可選值是否存在值,也可以通過可選值綁定按條件拆包,并在該可選值的值存在的情況下才訪問它的值。
有時根據程序結構可以知曉一個可選值在首次賦值后,必然存在值。這時,可以不必每次訪問時都檢測并提取該可選值的值,因為可以安全地假設它那時一定存在值。
這些可選值可定義為隱式拆包的可選值(implicitly unwrapped optional)。你可以通過在希望標為可選的類型名稱后面,用感嘆號 (String!
) 代替問號 (String?
)的方式書寫隱式拆包可選值。
隱式拆包的可選值在可選值首次定義后即確認存在值,在此之后任何時刻都能被明確地(肯定有值)使用。Swift 中隱式拆包可選值主要應用于類初始化,詳見 外部引用與隱式拆包的可選屬性(待譯)。
隱式拆包的可選值在運行環境里就是普通的可選值,但能夠像非可選值那樣使用,無需在每次訪問時拆包。下例顯示了 可選的 String
與 隱式拆包的可選 String
之間的行為差異:
1 let possibleString(可能的字符串): String? = "An optional String。(一個可選的String)"2 println(possibleString!) // 訪問其值時需要添加感嘆號3 // 輸出 "可選的 String。"4 let assumedString(假設的字符串): String! = "An implicitly unwraped optional String。(隱式的拆包的可選Stirng)"
5 println(assumedString) // 訪問其值時無需感嘆號
6 // 輸出 "隱式拆包的可選 String。"
可以認為,隱式拆包的可選值即授予可選值在被使用時自動拆包的權限(即無需再次手動進行拆包)。不必每次使用可選值時都在名稱后面添加感嘆號,只需在聲明時在可選值類型后面加上感嘆號即可。
注:如果在隱式拆包的可選值存在值之前就嘗試訪問(不存在值時不可進行隱式拆包),運行時會報錯。結果與在普通可選值尚未賦值時直接加感嘆號就引用相同。
隱式拆包的可選值也可以當作像普通可選值那樣,檢查是否存在值:
1 if assumedString {2 println(assumedString)3 }4 // 輸出 "An implicitly unwraped optional String。(隱式的拆包的可選Stirng)"
隱式拆包的可選值同樣可以使用可選值綁定,單條語句完成檢查值并拆包:
1 if let definiteString(肯定是字符串) = assumedString(假定是字符串) { 2 println(definiteString) 3 } 4 // 輸出 "隱式拆包的可選 String。"
注:
當變量在后續過程中可能變為
nil
時,不應使用隱式拆包的可選值。如果在變量聲明周期內都需要檢查nil
值,請務必使用普通的可選類型量。
謝謝,Swifter-QQ群:362232993,同好者進~
Fork:https://github.com/Joejo/Swift-lesson-for-chinese
|
新聞熱點
疑難解答