在OC中,我們通常會(huì)遇到這種寫法
NSString *urlString = @"";
NSURL *url = [NSURL URLWithString:urlString];
// NSError
NSError *error;
// 這里的 String初始化方法可能會(huì)創(chuàng)建失敗
NSString *result = [[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
只有result 等于 nil的時(shí)候 才應(yīng)該去判斷error是否為空
if (result != nil){
// do something ...
}
//只有result 等于 nil的時(shí)候 才應(yīng)該去判斷error是否為空
// 檢查 error ,非nil 的error 并不是一個(gè)有效的指針,直接使用可能造成異常
if (error) {
// error do something...
}
實(shí)際上OC中 向nil發(fā)送消息是安全的,但是這種方式顯得很繁瑣
Swift中的可選值
Swift中的枚舉 可以在它們的成員中包含另外的關(guān)聯(lián)的值,Optional 也是通過(guò)枚舉實(shí)現(xiàn)的
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
可選值的使用
完整的聲明如下:
var num1:Optional<Int> //Optional<Int>
num1 = .some(10) // Optional(10)
num1 = .none // nil
由于可選類型在Swift中很常見(jiàn),有更簡(jiǎn)潔的聲明如下,推薦?。。?/p>
Optional<Int> 可以寫成Int?,并且Optional遵守ExpressibleByNilLiteral協(xié)議可以用 nil 替代 .none
var num2:Int? // Int?
num2 = 10 //Optional(10)
num2 = nil //nil
num2? = 10 // nil
注意,這里的num2? = 10,當(dāng)num2的真實(shí)值不為nil的時(shí)候,num2? = 10才會(huì)賦值成功,否則就是nil,這是可選鏈調(diào)用
包裝一個(gè)非可選值,非可選值轉(zhuǎn)換為可選
var ch:Character = "4" // Character
var optionalCh = Optional(ch) //Character?
獲取枚舉關(guān)聯(lián)值的唯一方法是通過(guò)模式匹配,枚舉的模式匹配
switch num1{
case .none: break //none 什么都不做
case .some(let value):print(value)
}
這種通過(guò)模式匹配看著很有些復(fù)雜,Optional這個(gè)枚舉還能簡(jiǎn)化一下:
switch num1{
case nil:break
case let v?:print(v)
}
可選值綁定
用枚舉的模式匹配處理可選類型是有些復(fù)雜,可選類型作為Swift的一大亮點(diǎn),系統(tǒng)當(dāng)然提供了更加便利的方案:
if - let
if - let 語(yǔ)句會(huì)檢查可選值是否為 nil,如果不是 nil,便會(huì)解包可選值。并且它可以是多個(gè)表達(dá)式的組合,可選值的解包結(jié)果可以作為后續(xù)表達(dá)式的值,解包后的作用域介于{}之中
if let num = num1 , num > 0{
// do num operator ...
}
當(dāng)然if - let 語(yǔ)句也支持多個(gè)可選值的解包
if let lnum = num1 , rnum = num2{
// do lnum and rnum operator ...
}
if 后面可以是 var , if - var 。var對(duì)應(yīng)的解包值可以在作用域內(nèi)修改,但這是對(duì)原解包結(jié)果的拷貝,實(shí)際并不會(huì)修改原始值
let num1:Int? = 10
if var num = num1 {
num += 1
print(num) // 11
}
print(num1) // Optional(10)
while - let
while let 語(yǔ)句和 if let 非常相似,同樣 let可以是var,可以是多個(gè)表達(dá)式的組合,唯一不同的是它表示當(dāng)表達(dá)式的結(jié)果返回 nil 時(shí)便終止循環(huán)
//readLine()從“readLine 函數(shù)從標(biāo)準(zhǔn)輸入中讀取內(nèi)容,并返回一個(gè)可選字符串。當(dāng)?shù)竭_(dá)輸入末尾
//時(shí),這個(gè)函數(shù)將返回 nil”
while let n = readLine(){
print(n)
}
guard - let
guard let 更像是 if not let 的反義,可選解包不為nil的作用域從當(dāng)前guard語(yǔ)句開(kāi)始一直到離開(kāi)作用域前。在 guard 的 else 代碼塊中,你可以執(zhí)行任意代碼,但是必須提供一個(gè)離開(kāi)當(dāng)前作用域的方式,例如:return,異常拋出fatalError()
func doSomething(_ value:Int?){
guard let v = value else{
return
}
//v的作用域一直到函數(shù)結(jié)束
print(v)
//...
}
可選鏈
在OC中對(duì)nil對(duì)象發(fā)送消息是安全的,Swift中可選鏈也可以做到
delegate?.handle() //delegate 如果為nil handle將不會(huì)被執(zhí)行
OC中的Block在調(diào)用之前需要判斷是否為null,否則也會(huì)crash,Swift中的可選鏈在調(diào)用閉包也是沒(méi)問(wèn)題的
let block:((String) -> Int)? = nil
block?("123")
調(diào)用可選鏈得到的返回值會(huì)自動(dòng)包裝成可選類型
let result = block?("123") //Int?
多個(gè)可選鏈的結(jié)果,編譯器會(huì)幫我們展開(kāi),
extension Int{
var opt:Self?{
Optional(self)
}
}
//以下調(diào)用結(jié)果不是 Int??? 而是Int?
var op = 5.opt?.opt?.opt // Optional(5)
可選鏈鏈賦值,可選變量賦值,如果加上?會(huì)判斷當(dāng)前變量是否為nil,如果為nil就會(huì)賦值失敗
var num2:Int? = nil // 不初始化 默認(rèn)是也nil
num2? = 10 // nil
num2 = 20 // 無(wú)條件賦值
num2? = 30 // Optional(30)
nil合并運(yùn)算符
剛開(kāi)始在使用可選值的時(shí)候,編譯器會(huì)有這樣的提示:Provide a default value to avoid this warning這句話意思是需要提供一個(gè)如下形式的默認(rèn)值:nil合并運(yùn)算符 ??
num1 ?? default value // 需要提供一個(gè)默認(rèn)值
注意??兩側(cè)的類型必須一致,左側(cè)必須是可選類型。比如左側(cè)是Optional(Int),右側(cè)必須是Int類型
看一個(gè)例子:這行代碼,如果不加 ??是不能編譯通過(guò),因?yàn)樽址D(zhuǎn)int有可能失敗,Int.init()返回值是個(gè)可選值,如果Int("123")初始化成功,返回解包后的值,失敗返回??后面的值。
var number:Int = Int("123") ?? 0
其中的??的行為類似于三目運(yùn)算符:
var intValue = Int("123") //Optional(Int)
number = (intValue!) != nil ? intValue! : 0
多個(gè)可選值通過(guò)??鏈接,表達(dá)式的結(jié)果是選擇第一個(gè)不為nil的結(jié)果
var a:Int? = nil
var b:Int? = 2
var c:Int? = 3
var result = a ?? b ?? c ?? 0 //2
強(qiáng)制解包
上文提到的解包方式都是可選解包,只有當(dāng)結(jié)果不為nil的時(shí)候,才會(huì)進(jìn)行下一步。而強(qiáng)制解包是通過(guò)在可選值后面加上!,這種方式看似簡(jiǎn)潔,但是會(huì)忽略編譯器警告,而且當(dāng)結(jié)果為nil時(shí)會(huì)直接閃退
var number = Int("123")! // 123
var number1 = Int("123a")! // Fatal error: Unexpectedly found nil while unwrapping an Optional value
強(qiáng)制解包時(shí)機(jī)
當(dāng)你能確定你的某個(gè)值不可能是 nil 時(shí)可以使用嘆號(hào),你應(yīng)當(dāng)會(huì)希望如果它意外是 nil 的話,程序應(yīng)當(dāng)直接掛掉
隱式解包
隱式解包,是在聲明類型的時(shí)候,加上!。注意區(qū)別強(qiáng)制解包
這種方式聲明的變量,在使用的時(shí)候系統(tǒng)會(huì)強(qiáng)制解包,因此看上去不像是一個(gè)可選類型。但是一旦值為nil,使用時(shí)候依然會(huì)異常(使用的時(shí)候系統(tǒng)會(huì)強(qiáng)制解包)。
let number3:Int! = Int("123") // Int!仍然是可選類型
number3 + 1 // 124 系統(tǒng)強(qiáng)制解包number3,如果number3為nil,閃退。否則正常運(yùn)行
隱式解包,仍然是可選類型,可以對(duì)它進(jìn)行可選解包
if let number4 = number3{
number4 // int
}