Swift可選類型詳解

在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
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容