Swift17/90Days - 可選類型

Swift90Days - 可選類型

(對於可選類型的理解能夠參見貓神的這篇行走於 Swift 的世界中,在此整理一些關鍵的部分。)git

Optinal Value

Swift 中的 Optinal Value 就像是一個盒子,可能裝着值,也可能什麼都沒裝。github

咱們能夠用 ? 定義一個可選類型的整數 :swift

var num: Int?  
num = nil  //nil  
num = 3    //{Some 3}

可選類型的真實身份實際上是 Optional 類型,經常使用的 ? 是 Apple 提供的語法糖。使用 Optional 的寫法是這樣的:app

var num: Optional<Int>
num = Optional<Int>()   // nil
num = Optional<Int>(3)  // {Some 3}

點進去看下 Optional 的定義:ide

enum Optional<T> : Reflectable, NilLiteralConvertible {
    case None
    case Some(T)

    /// Construct a `nil` instance.
    init()

    /// Construct a non-\ `nil` instance that stores `some`.
    init(_ some: T)

    /// If `self == nil`, returns `nil`.  Otherwise, returns `f(self!)`.
    func map<U>(f: (T) -> U) -> U?

    /// Returns a mirror that reflects `self`.
    func getMirror() -> MirrorType

    /// Create an instance initialized with `nil`.
    init(nilLiteral: ())
}

這樣一來,var num: Int? = 3 其實就是 Optional.Some(3)post

在這裏,? 聲明瞭一個 Optional<T> 類型的變量,而後作了判斷:.net

  • 若是等號右邊是 nil ,則賦值爲 Optional.None
  • 若是等號右邊不是 nil ,則經過 init(_ some: T) 初始化並返回 Optional.Some(T)

Force Unwraps

在遭遇可選類型的時候,咱們能夠在 Optional 變量後面加上 ! 進行強制解包。這樣雖然減小了代碼量,可是容易帶來隱患,使用的時候務必要慎重。就像是過馬路同樣,必定要仔細看好兩邊車輛,不然悲劇隨時可能發生。code

而實際上,不少時候能夠避免使用 ! ,用其餘方法取而代之。排序

來看這樣一個例子:ci

let ages = [
    "Tim":  53,  "Angela": 54,  "Craig":   44,
    "Jony": 47,  "Chris":  37,  "Michael": 34,
]
let people = sorted(ages.keys, <).filter { ages[$0]! < 50 }
println(people) // "[Chris, Craig, Jony, Michael]"

在這裏先對 ages 進行了排序,而後篩選出年紀小於 50 的人。由於是對字典取值,會出現 nil ,因此 ages[$0] 是 optional 的,須要進行解包。

固然也能夠用 if let

let people = sorted(ages.keys, <).filter {
    if let age = ages[$0] {
        return age < 50
    }
    return false;
}

可是原本一行就能搞定的問題卻拖沓到了五行才解決。實際上,換個思路,咱們徹底不須要遭遇可選類型:

let people = filter(ages) { $0.1 < 50 }.map { $0.0 }.sorted(<)

filter 中 $0 表示傳入的 key-value ,$0.1 表示 value,map 中 $0 表示傳入的 key-value ,$0.0 表示 key 。或許這樣可讀性比較差,能夠經過 tuple 包裝一下入參:

let people = filter(ages) { (k,v) in v < 50 }.map { (k,v) in k }.sorted(<)

在平時的開發中,咱們能夠用 if let 或者 ?? 替換掉 ! 。若是確實確實確定不會出問題再去用 !

Implicitly Unwrapped Optionals

一樣是 ! 符號,若是放在類型後面,則表示隱式解析可選類型 (Implicitly Unwrapped Optionals):

var num: Int!
num = 1     // 1
num = nil   // nil

經過 ! 定義的變量,實質上只是對 Optional 的變量多了一層封裝,幫咱們完成了本來須要手動解包的過程。

在什麼場合下可使用隱式解析可選類型呢?

場景1:沒法在初始化方法中定義的常量

有些尷尬的狀況,咱們想定義常量屬性,無奈它的初始化依賴於其餘屬性值。若是你用可選類型,實在是麻煩,由於你確信無疑它確定會在調用以前就完成初始化,不多是 nil ,這個時候你能夠用 ! 進行定義。

舉個例子:

class MyView : UIView {
    @IBOutlet var button : UIButton
    var buttonOriginalWidth : CGFloat!

    override func viewDidLoad() {
        self.buttonOriginalWidth = self.button.frame.size.width
    }
}

這部份內容在 Day11 中有涉及,其實更好的方法是用 lazy 延時加載,再也不贅述。

場景2:與 objc 進行交互

隱式解析可選類型彷佛是爲了照顧 objc 這個歷史包袱而存在。好比 UITableView 中:

override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell? { return nil }

咱們很清楚的知道,在調用的時候 tableViewindexPath 不可能爲 nil ,若是還要 if let 這樣解包實在是浪費時間。若是是純粹的 Swift 語言寫的,絕對不會定義成 optional 類型。

何時不應用

除了上述的狀況2 ,其餘狀況都不應用 (包括狀況1)。


References

相關文章
相關標籤/搜索