[swift 進階]讀書筆記-第四章:可選值 1_3 序列_魔法數問題_可選值概覽

liaoWorking在掘金發表於2018-12-24

可選值

4.1哨崗值 Sentinel Value


定義:有的函式可能返回nil 當函式返回nil 我們將返回值 用作-1 等類似的值代替,這樣的值就被稱為"哨崗值" 也叫魔法數。

4.2通過列舉來解決魔法數的問題


swift中Optional的本質是一個列舉(目前可以先不具體思考,等以後的專案中慢慢就領悟到了)。

enum Optional<Wrapped> {
    case none //為空
    case some(wrapped) // 有值
}
複製程式碼

知識點:當返回值為可選值 ? 時,實質上返回的是一個Optional的列舉

    ///返回值為Optional的實質
    let strArray = ["one","two","three"]
    switch strArray.index(of: "four") {
    case .none:
        print("返回值為空") //返回值為空
    case .some(let idx):
        print(idx)
        print("有值")
    }
複製程式碼

4.3可選值概覽:Optional

if let

相信大家在swift 入門的時候就已經能很好的使用這個語法了。 不贅述。

技巧1: 可與Bool 聯合判斷

if let obj = Optional , Bool {
    /// 前面有值且後面Bool為true執行
}
複製程式碼

技巧2: 多let 並列。 這個在開發中也經常用到

if let obj1 = Optional1, let obj2 = Optional2, ....{
///當上面的let判斷都有值的時候執行
}
複製程式碼

while let

類似於if let 遇到nil時終止迴圈 也有和上面?技巧1(一旦為false 就迴圈停止,並不是filter那樣去工作)相同的使用方法。不贅述。

    while let line = printLine() {
    ///當printLine為空時候終止迴圈
        print(line)
    }
複製程式碼

雙重可選值:


一個可選值可能會被另外一個可選值包裝起來,形成了可選值的巢狀。你也可以理解成obj?? 。平時在複雜邏輯操作時注意一哈就可以了。

if var 和 while var

和 let 的使用類似。 但你var 出來的obj實際上是一個本地副本 你對obj做任何更改都不會影響原來的值。

 ///if var 的改變不影響原值
        let dict:[String:String]?
        dict = ["key1":"value1"]
        if var varDict = dict {
            varDict["key1"] = "value2"
        }
        print(dict) //Optional(["key1": "value1"])
複製程式碼

解包後可選值的作用域

if let obj = Optional {
///obj的作用域
}
複製程式碼

注:有時候我們需要提前退出來避免煩人的if巢狀,這個時候 guard let 的優點馬上就體現出來了.

///為空提前退出
guard let obj = Optional else{return}
複製程式碼

可選鏈

OC中對nil傳送訊息,nothing had happened。 在swift中可以通過可選鏈(Optional chaining)達到一樣的效果

那麼問題來了,什麼是可選鏈? 可選鏈 中的可選是?,可以把鏈理解成swift中的點語法,可以在《swift鏈式程式設計》一書中對這個有更深的理解

delegate?.callback() //就是一個可選鏈。

//當上面的delegate為可選時編譯器會發出警告。 確實有值方法會被呼叫。沒有值時候方法不會被呼叫。
複製程式碼

ps. ?寫了這麼多年swift的delegate和閉包 直到自己寫筆記的時候才發現自己寫複雜了(一點swift的優點都沒體現出來。。。)

var voidCallback:(()-> Void)?
    
    ///對於閉包呼叫的兩種寫法
    //❌不推薦
    if voidCallback != nil {
        voidCallback!()
    }
    
    //✅ 推薦
    voidCallvack?()
複製程式碼

多個可選鏈的呼叫。下面這個例子簡單易懂,不贅述。

extension Int {
    
    var half: Int? {
        guard self > 1 else {return nil}
        return self/2
    }
    
}
///多可選鏈呼叫
20.half?.half?.half //Optional(2)
複製程式碼

nil合併運算子 ?? (專案中經常會用到!!可以讓你的程式碼寫的很優美~)


一開始看到標題nil合併運算子的時候我是?的。這都是啥啥啥啊。 當我看到 ?? 這個符號時候豁然開朗。兩個是一個意思 專案裡面已經用過很多次了,百試不爽。

使用場景: 當去解包!一個可選值,且當這個可選值為nil時,我們要去賦一個預設值防止崩潰 這個時候就用到了 ??

    var number: Int?
    number = nil
    String(number ?? 5) //5 
複製程式碼

感覺上是和OC 中的 三目運算子 Bool ? A: B 類似 需要強調的是可選值不是指標

可選值map

可選值flatMap(Demo值得借鑑)

上面兩小節講的都是map 和flatMap對於可選值的一個用法。 有一個小demo對於日常開發可以說是有著四兩撥千斤的感覺,如下:

    ///將一個網路圖片載入出來的奇淫方法!!!
    let urlString = "http://www.objc.io/logo.png"
    let view = URL(string: urlString)
        .flatMap { (url) -> Data? in
        try? Data(contentsOf: url)
        }
        .flatMap { (data) -> UIImage? in
            UIImage(data: data)
        }
        .map { (image) -> UIImageView in
            UIImageView(image: image)
    }
    
    if let view = view {
        UIView().addSubview(view)
    }
複製程式碼

利用swift中的$0語法簡化上面的程式碼

    let view2 = URL(string: urlString)
        .flatMap {
            try? Data(contentsOf: $0)
        }
        .flatMap {
            UIImage(data: $0)
        }
        .map {
            UIImageView(image: $0)
    }
    
    if let view2 = view2 {
        UIView().addSubview(view2)
    }
複製程式碼

這裡的flatMapif let 非常相似。 這樣我們就將一個網路圖片載入出來了。 想想自己之前寫了那麼多的垃圾程式碼,不禁長嘆一句:swift??!!!

使用flatMap過濾nil

同樣這裡我們用一個demo去說明用法

目的:我們想要求一個字串陣列中的數字和。

    let numbers = ["1","2","3","4","liaoworking"]
    ///普通青年
    var sum = 0
    for case let i? in numbers.map({
        Int($0)
    }) {
        sum += i// Int($0)為nil就不走這裡了
    }
    //        sum的值為10
    
    ///優質青年
    ///當我們用?? 把nil替換成0
    numbers.map { Int($0) }.reduce(0) { $0 + ($1 ?? 0)} //10
    
    ///文藝青年
    ///在標準庫中flatMap的作用可能正是你想要
    numbers.flatMap { Int($0) }.reduce(0, +) // 10
複製程式碼

注:這裡的flatMap的作用:把一個對映為可選值的序列進行展平。

這個例子的確好吊,不自己寫一遍不知道還有很多高階用法自己不會。 學習了學習了~

如果讓我去面試swift開發。一定會選這個demo做一個面試題!

可選值判等

判等時我們並不關心可選值是不是nil,只用去管它是否包含我們想要的非nil的特定值即可。

switch case 匹配可選值

/// switch case
    let numbers = ["1","2","3","4","liaoworking"]
    for i in numbers {
        switch Int(i) {
        case 0:
            print("0")
        case nil:
            print("can't convert to int")
        default:
            print("not in case")
        }
    }
複製程式碼

swift中case匹配是通過~=運算子進行的。 我們可以實現這個方法增加對範圍(0..<num)匹配的方法

###可選值的比較 和 == 類似 可選值曾經也有 <=, >=, <, >操作符 後來在SE-0121因為安全原因被移除了。

///在一個溫度陣列中
let temp = ["-23", "0", "66.66", "warm"]

Double("warm") 的值為nil

Double("warm") < 0 是成立的

但你不能說warm是小於0度的是很冷的。
複製程式碼

這顯然不合情理。

文章原始檔地址

相關文章