如何理解Swift中Optional的!和?

很多人在剛上手swift時(shí)對(duì)于Optional中 ‘!’ 和 ‘?’ 的使用十分不理解,接下來我會(huì)談一談自己對(duì)于這兩個(gè)符號(hào)的使用方式的理解。

先來說說Optional的概念,以方便之后的理解。來看一下下面的代碼:

var a : String = nil              // 編譯錯(cuò)誤,String類型不能為nil
var b : String = “Hello!”

Swift 中的普通類型不再能設(shè)為nil。那如何表示這個(gè)值不存在呢?所以引進(jìn)了Optional的概念:代表 nil 或某個(gè)具體的值。例如:

var c : String? = nil       
var d : String? = “Hello!”

String? 就是一個(gè)Optional,它既能夠被具體類型賦值, 也可以賦值為nil。通過 String 和 Optional的比較,發(fā)現(xiàn)Optional 就相當(dāng)于把具體類型和 nil 打包捆綁在了一起,轉(zhuǎn)變成了一種新的類型。

Optional 有兩種聲明方法:

var apple: String?   
var bread: String!

很多人認(rèn)為,聲明為 String! 的變量表明該變量的值不為 nil。但是,實(shí)際上,String! 和 String? 都是有默認(rèn)值的,且默認(rèn)值都為nil。我們?yōu)樗鼈冑x一個(gè)初值再打印類型來看看:

var apple: String? = “apple”   
var bread: String! = “bread”
(lldb) p apple
(String?) $R0 = “apple”
(lldb) p bread
(String?) $R1 = “bread”

發(fā)現(xiàn),盡管apple 和 bread 雖然一個(gè)是 String? 一個(gè)是 String! 但是打印出來都是 String? 類型的。所以更恰當(dāng)?shù)睦斫鈶?yīng)該是:String! 只是理解意義上的不為nil,其本質(zhì)還是一個(gè) Optional,從聲明來說它和 String? 完全等價(jià),所以也能夠賦值為 nil 。
為了之后敘述方便,我們把定義類似 String? 的稱為 Optional(?), 定義類似 String! 的稱為 Optional(!)。

Optional 的本質(zhì)是囊括 nil 和具體類型的一種枚舉。獲取它具體值的操作過程稱之為拆包,用 “!” 表示。先來做個(gè)實(shí)驗(yàn):

var string : String
var optionalString: String?   
var nonOptionalString: String!
string = nonOptionalString    // ① 崩潰
string = optionalString      // ② 編譯錯(cuò)啦
string = optionalString!     // ③ 崩潰

具體看一下報(bào)錯(cuò)原因:

① String! 賦值給 String ,報(bào)錯(cuò)為:拆包時(shí)Optional的值為nil;
② String? 賦值給 String,編譯器報(bào)錯(cuò)為 Optional未拆包;
③ 拆包后的 String? 賦值給 String,這時(shí),報(bào)錯(cuò)為:拆包時(shí)Optional的值為nil;

String! 和 拆包后的 String? 進(jìn)行傳值時(shí)報(bào)的錯(cuò)相同,由此我們可以得出一個(gè)結(jié)論:編譯器默認(rèn) Optional(!) 是確定有值的,所以不需要再對(duì) nil 的情況進(jìn)行處理。Optional(!) 的變量只是給予了自動(dòng)拆包的權(quán)限,在實(shí)際使用它的過程中不需要再次使用 ‘!’ 進(jìn)行拆包。在swift官方文檔中稱為:Implicitly Unwrapped Optional (隱式拆包),也可以理解成“拆過包了”。

但是,只有在變量確認(rèn)有值的情況下才能進(jìn)行拆包。就像如上代碼,optionalString 為nil,進(jìn)行拆包就會(huì)引發(fā)崩潰。swift官方建議,Optional 使用 if + ! 結(jié)合的方式或者 if let 進(jìn)行安全地拆包。簡(jiǎn)單的看下代碼:

if optionalString != nil {
   append(string: optionalString!)
} else {
   // do something
}
if let actualString = optionalString {
   append(string: actualString)
} else {
   // do something
}

再次提醒一下,Optional(!) 和 Optional(?) 都可以使用這兩種方式進(jìn)行安全地拆包,只是編譯器不會(huì)對(duì)沒有處理 nil 情況的 Optional(!) 報(bào)錯(cuò)。

下圖展示了 Optional和 String 可接收類型的比較:

拆完包之后的 Optional其實(shí)就是 String 類型。編譯器強(qiáng)制使用者在變量為 nil 的時(shí)候要進(jìn)行處理,否則就會(huì)報(bào)錯(cuò)會(huì)崩潰。String! 是為了規(guī)避變量一定不為 nil 的情況下卻要反復(fù)判斷是否為 nil 的冗余代碼而產(chǎn)生的。例如,我們?cè)谑褂?IBOutlet 時(shí),一定會(huì)定義成 Optiona(!)。String! 在聲明時(shí)和 String? 完全等價(jià),在使用時(shí)和 String 完全等價(jià)。

總結(jié)一下:

  1. Optional的本質(zhì)是一個(gè)包含了 nil 和普通類型的枚舉,這是為了確保使用者在變量為 nil 的情況下會(huì)完成相應(yīng)的處理;

  2. 無論是 Optional(!) 還是 Optional(?) 都是一種Optional,在未設(shè)初始值時(shí),默認(rèn)為nil。Optional(!) 只是給予了自動(dòng)拆包的權(quán)限,省略了判斷其值是否為nil的過程,但是不能夠保證它的值不為nil;

  3. Optional(!) 在聲明時(shí)和 Optional(?) 等價(jià),在使用時(shí)和具體類型等價(jià);

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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