在Swift代碼會經(jīng)逞晾鳎看到定義屬性或方法參數(shù)時類型后面會緊跟一個感嘆號( ! )或問號( ? ), 剛開始接觸Swift的童鞋就可能不太明白之代表什么意思玄妈,一頭霧水茬暇,開始凌亂了霹期。
本文將帶你了解感嘆號( ! )與問號( ? )之謎谆棱,首先問號( ? )是可選類型快压,是用來處理值可能缺失的情況,也就是沒有值的情況(也就是OC中NULL); 感嘆號( ! )就是與之相反的, 就是一定有值(非可選類型)垃瞧,不存在空值的情況(OC中NULL)嗓节。
可選類型(Optional Type)
常量或變量聲明的時候類型后面帶有?或!為可選類型
- 聲明為Optional常量時,如果沒有賦初始值皆警,次常量沒有默認值拦宣,需要在構(gòu)造函數(shù)中給常量設置初始值
- 聲明為Optional的變量,如果不顯示的賦值就會有個默認值nil
- 聲明為Optional的變量或常量不能直接參與運算信姓,必須解包后才能參與運算
- 解包后的Optional變量或常量如果沒有值(值為nil)參與運算會發(fā)生Crash
- 可選類型類似于Objective-C中的nil鸵隧,但是nil只針對類有用,而Swift中可選類型對所有的類型都可使用意推,并且更安全
下面根據(jù)不同使用場景來進一步了解感嘆號( ! )與問號( ? )之謎
強制展開
如果一個可選有值豆瘫,也就是它不等于nil, 可以通過比較nil來判斷一個可選中是否包含值
一旦確定可選中包含值,可以使用后綴操作符!來強制拆包訪問這個值菊值,感嘆號的意思就是說"我知道這個可選項里面有值外驱,放心使用吧"這就是所謂的可選值的強制展開
var myString:String?
myString = "Hello, Swift!"
if myString != nil {
print(myString) //prints:Optional("Hello, Swift!")
// 強制展開
print(myString!) //prints:Hello, Swift!
}else{
print("myString 值為 nil")
}
如果使用!來獲取一個不存在的可選值會導致Crash,因此在使用!強制展開之前必須確蹦逯希可選項中包含一個非nil的值昵宇。
可選項綁定
可以使用可選項綁定來判斷可選類型是否包含值,如果包含就把值賦給一個臨時的常量或變量儿子⊥甙ィ可選綁定可以與if和while的語句使用來檢查可選項內(nèi)部的值,并賦值給一個變量或常量柔逼。
var myString:String?
myString = "Hello, Swift!"
if let yourString = myString {
print("你的字符串值為 - \(yourString)")
}else{
print("你的字符串沒有值")
}
//prints:你的字符串值為 - Hello, Swift!
隱式展開
隱式展開可選項:通過在類型后面添加一個嘆號(!)而不是問號(?)
你可以把隱式展開可選項當作每次訪問它的時候被給予了自動進行展開的權(quán)限(編譯器自動在操作前補上一個!進行拆包蒋譬,然后在執(zhí)行后面的操作,當然如果改值為nil愉适,也一樣會報錯發(fā)生Crash)犯助,這樣就可以在聲明可選項的時候添加一個嘆號,而不是每次調(diào)用的時候在可選項后邊添加一個嘆號维咸。
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation mark
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // no need for an exclamation mark
你可以像對待普通可選一樣對待隱式展開可選項來檢查里邊是否包含一個值剂买,也可以通過可選綁定來檢查和展開隱式展開可選項:
//檢查可選項是否包含一個值
if assumedString != nil {
print(assumedString)
}
//通過可選綁定來檢查和展開隱式展開可選項
if let definiteString = assumedString {
print(definiteString)
}
什么時候使用隱式解析可選類型
- 初始化過程中不能確定有正確的值扑媚,但是又可以確保在被訪問前一定會有值。
- 使用可選變量不會有問題雷恃,因為在可選變量在初始化時會自動賦予nil疆股,并且在初始化結(jié)束時被賦予正確的值。但是使用時的強制解包會很痛苦倒槐,滿屏都是!
以下展示了適合使用隱式解析可選選項的例子:
class MyController: UIViewController {
@IBOutlet var button: UIButton!
var buttonOriginalWidth: CGFloat!
override func viewDidLoad() {
self.buttonOriginalWidth = self.button.frame.size.width
}
}
在這里變量button不是在初始化時賦值的旬痹,而是在加載視圖的時候。你可以把它設置為普通可選類型讨越,但是如果這個視圖加載正確两残,它是不會為空的。
在視圖加載完成之前把跨,我們無法知道button的初始化寬度人弓,但是我們知道viewDidLoad方法會在任何方法之前被調(diào)用(除了初始化函數(shù))。與其在所有地方強制解包着逐,不如定義成隱式解析可選類型崔赌。
合并空值運算符
上面對可選類型的三種處理不是需要繁瑣的判斷就是存在Crash風險不夠安全,那么??的出現(xiàn)很好的解決了這個問題耸别。
合并空值運算符 (a ?? b )如果可選項 a 有值則展開健芭,如果沒有值,是 nil 秀姐,則返回默認值 b 慈迈。表達式 a 必須是一個可選類型。表達式 b 必須與 a 的儲存類型相同省有。
合并空值運算符是下邊代碼的縮寫:a != nil ? a! : b
let defaultColorName = "red"
var userDefinedColorName: String? // defaults to nil
var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName is nil, so colorNameToUse is set to the default of "red"
異常處理 try? 和 try!
try?: 如果不想出處理異常痒留,那么可以使用這個關(guān)鍵字,使用這個關(guān)鍵字會返回一個可選類型蠢沿,如果有異常出現(xiàn)伸头,返回nil,否則返回可選值搏予。例如:
enum MyError : Error {
case one
case two
case three
}
func testFunc(str: String) throws -> String {
if str == "one" {
throw MyError.one
}else if str == "two" {
throw MyError.two
}else if str == "three" {
throw MyError.three
}
return "ok"
}
var str = try? testFunc(str: "three")
print(str)
控制臺輸出:
nil
try!: 有點類似NSAssert(),使用try!后熊锭,一旦可能拋出異常的方法中拋出了異常弧轧,那么程序會立刻停止