天天看點

Swift的Optional類型

我們使用Swift這個蘋果新推出的程式設計語言已經有一段時間了。其中的一個極大的優點就是蘋果稱為“optional types”的東西。幾乎所有的objective-c程式員都知道用nil來表示某個引用類型的對象是沒有值的。但是要把nil和某個變量的類型聯系起來還是有些牽強。

這裡,我們就來介紹一下Swift提供的optional type(可選類型)。先介紹一些實作的細節,然後指出optional type體系裡的幾個要點。

類型?

在我們開始進入代碼前,先來看看為什麼一個類型被定義為可選的。我們遇到的類型一般都是通常的,非可選的類型。包括一般的值類型,比如,Int,Bool和String。還有複雜點的引用類型,比如,UIView,等。

我們聲明這些類型的變量的時候,Swift要求必須給這些變量指派。這一要求非常的嚴格,如果你想在初始化一個變量之前使用它使編譯不過的。 

Swift的Optional類型

這個表面看起來很郁悶,但其實很有幫助。長遠來說,不讓這樣的代碼編譯通過,Swift可以避免發生因為使用了未初始化的值而引發的潛在的運作時錯誤。

let x: Int = nil

然而,有人會嘗試給let聲明的常量指派為nil。 

Swift的Optional類型

這樣的代碼會直接引發一個錯誤:類型“Int”不是protocol ’NilLiteralConvertible’類型。對于非optional type的類型,不實作“NilLiteralConvertible”protocol是不可以使用nil來初始化的。是以,簡單的x,隻是一個Int型的執行個體,隻能被指派為Int的值而不是nil。

程式裡的變量不可能全部都有初值,是以這個時候optional type就該出場了。Swift裡,隻要在任意類型的後面加一個問号以後就變成了optional type(可控類型)。比如,之前的例子中。隻要給Int後面加一個問号就可以将x指派為nil了。 

Swift的Optional類型

這應該是很多寫Objective-C的哥們需要的了。變量值可以是“實際”的值,也可以是nil。這隻取決于你的代碼處理的是哪種情況了。

裝箱

你可能會說,“Int隻是值類型,不是一個對象怎麼能使用nil呢?NSInteger是不能這麼用得。。”

的确,你說的沒錯。NSInteger沒有nil值。或者更準确的說經過類型轉化後你會得到一個整型值0。是以,在Objective-C得API裡定義了很多的标記表明“無值”狀态:0,1,NSIntergerMin,NSIntegerMax以及NSNotFound等,都是表明“nothing”的。 

當你仔細考慮就會發現沒有一個一緻的方式表明“nothing”這個值,而是用不同的自定義值标記“nothing”會增加一定的複雜度。取一個數組中不存在的值傳回的事NSNotFound,取一個table view中不存在的row的時候傳回的事一個-1。

Swift的optional type提供了一種更加清晰的表示方法。但是如何讓任何類型都具有optional type這個功能呢?這些都建立在泛型的基礎之上: 

Swift的Optional類型

上面的就是Swift核心庫對于optional type的定義(稍微作了修改)。Swift定義了一個新的類型Optional,它由兩個值,一個是“nothing”,叫做None,一個是把某個類型T的值給包裝起來之後的值。Swift就是利用這一機制把某了類型的值包裝起來,當然這個值可以是空(nothing),也可以不是空。

Swift的Optional類型

在這個例子中, 第一個整數就隻是一個Int型的值。後面的類型後面跟了問号“?”的其實都是Optional<T>類型的。或者,簡短的可以表示為Int?。

有了這個能力,Swift就可以給任何類型表示“nothing”的值nil,即使是Int。Optional type同時可以表示“real”的值,也可以表示“nothing”的值,而不需要其他的特殊的值。

拆箱

這樣表示optional value同時也會引發一個問題。現在我們知道optional type是一個獨立的類型:Optional<T>。是以,在需要T的地方,不如某個函數需要T類型的參數傳入,那麼optional<T>的值是不能用的: 

Swift的Optional類型

我們需要把需要的值從optional的箱子裡拿出來。并且,很重要的一點,在那之前需要檢查這個值是否存在。Swift提供了歎号“!”操作符來提取值。 

Swift的Optional類型

記得把x的值修改為100,而不是之前的nil。因為,歎号操作符隻适用于optional type的值本身有“real”值的。如果沒有的話是會抛出運作時異常的。

是以,在拆箱取值以前,我們需要先判斷這個可選(optional)的值是否為空。就和我們在Objective-C中常做的類似。 

Swift的Optional類型

但是如果這個x是從其他的方法傳回回來的呢?我們可以直接調用這個函數來檢驗傳回值, 完全不必要先給局部變量指派,再檢測是否為空。

Swift已經實作了這個功能,叫做optional binding(可選綁定)。使用if和let兩個關鍵字就可以寫出一行緊湊的代碼來檢測函數傳回值是否存在。

Swift的Optional類型

這裡我們已經不用歎号操作符來顯示的強制拆箱。這是optional binding(可選綁定)的另一個好用的地方。直接在if語句的判定表達式裡拆箱optional type(可選類型),就可以确定這個optional type是否有值,不用手動的使用歎号操作符來拆箱。

Chaining

現在我們建立一個準确的檢測和拆箱optional value(可選值)的規則。比如,如何在optional value上調用方法?你肯定會在一個可能為空的對象上調用方法,這是一定會發生的。在Objective-C中,在nil對象上調用方法會傳回一個nil。

幸好Swift也可以做到這樣。使用optional chaining(可選鍊)的方式來調用可能為空(nil)的方法: 

Swift的Optional類型

在對象和其調用的方法之間插入一個問号“?”操作符,我們就可以表明是要一個實際存在的值還是要一個“nothing”。這和Objective-C的調用非常類似。

注意:這樣調用的方法的傳回值一定都是optional type(可選類型)的,即使這個方法的傳回值被定義為非可選類型(non-optional type)。是以,在optional value(可選值)上調用的方法鍊上得任意一點的傳回值都是optional的。在處理傳回值的時候一定要考慮到值可能為空的可能。

考慮下面的代碼: 

Swift的Optional類型

someMethod()方法的聲明中制定傳回值為Int型,z還是得到一個optional value(可選值)。因為,我們使用了optional chaining(可選鍊)來調用方法。這可能看起來有點迷惑,但是很有幫助。尤其是在optional binding(可選綁定)的時候。比如,上面代碼中的if let z = y?.someMethod()表達式。

這樣可以很簡潔的處理一下問題:

  • 如果y是nil(這裡已經是nil),optional chaining(可選鍊)可以保證我們這樣寫代碼而不報錯
  • 如果y是nil或者someMethod()方法傳回nil,optional binding(可選綁定)不會把nil指派給non-optional value(非可選值)z。
  • 最終我們會得到z,但是不用手動拆箱。因為這是可選綁定的(optional binding)。

總之,對于處理nil值來說,Swift提供了一個非常清晰的系統。我們或得了額外的類型安全,避免了不必要的特殊定義的值,而且還是像Objective-C一樣簡潔。

歡迎加群互相學習,共同進步。QQ群:58099570 | 做人要厚道,轉載請注明出處!

繼續閱讀