Swifter 100 tips 讀后總結(jié)

Swifter 100 tips 讀后總結(jié)

  1. 將protocol的方法2為mutating,原因如下:

    1. protocol也適用于struct和enum中,如果不添加mutating,那么在struct和enum中將會提示沒有實(shí)現(xiàn)協(xié)議方法,無法通過編譯.
    2. 如果去掉protocol中的mutating字段,那么struct和中將會報(bào)錯(cuò)說不能改變成員變量.
    3. 在class 中實(shí)現(xiàn)protocol 方法的時(shí)候,不需要添加mutating字段,因?yàn)閏lass可以隨意更改自己的成員變量,所以在protocol里邊用mutating修飾方法,對于class的實(shí)現(xiàn)是完全透明的,可以當(dāng)做不存在.
  2. 多元組 (Tuple)

    這是一個(gè)oc中沒有使用過的結(jié)合類型,使用它可以幫助我們簡化很多問題,比如:

    1. 在oc甚至java中,交換2個(gè)變量的值,我們通常會這么寫:
    func swapMel<T>(a: inout T, b: inOut T) {
         let temp = a
         a = b
         b = temp
    }
    

    這樣做很容易理解,但是產(chǎn)生了一個(gè)中間變量,這是我們不想看到的結(jié)果,所以有人會這么寫:

    func swapMel<T>(a: inOut T, b: inOut T) {
         a = a ^ b
         b = b ^ a
         a = a ^ b
    }
    

    這樣,我們就沒有新增一個(gè)中間變量,就進(jìn)行了2個(gè)變量的值的交換,但是這樣做的代價(jià)就是不容易理解.當(dāng)我們用swift中的Tuple來實(shí)現(xiàn)就簡單多了,并且很容易理解:

    func swapMel<T>(a: inOut T, b: inOut T) {
         (a, b) = (b, a)
    }
    

    這樣我們就完成了2個(gè)變量的交換過程,很簡單.

    1. oc中的返回值只能有一個(gè),所以在解決某些問題的時(shí)候會顯得很麻煩,比如:

    CGRect有一個(gè)輔助方法CGRectDivide,它是將一個(gè)CGRect在一定位置切分成2個(gè)區(qū)域,具體使用方法如下:

    CGRect rect = CGRectMake(0, 0, 100, 100);
    CGRect small;
    CGRect large;
    CGRectDivide(rect, &small, &large, 20, CGRectMinXEdge);
    

    它將{0, 0, 100, 100}分割成了small:{0, 0, 20, 100} 和 large: {20, 0, 80, 100} 2個(gè)區(qū)域,

    下邊我們可以用swift的Tuple來實(shí)現(xiàn),并對比一下:

    extension CGRect {
         func divided(atDistance: CGFload, from fromEdge: CGRectEdge) -> (slice: CGRect, remainder: CGRect) {
             //...
         }
    }
    

    使用的時(shí)候,做法如下:

    let rect = CGRect(x: 0, y: 0, width: 100, height: 100)
    let (small, large) = rect.divided(atDistance: 20, from: .minXEdge)
    

    這樣看起來就很簡單明了

  3. @autoclosure 和 ??

    1. @autoclosure做的事情就是吧一句表達(dá)式自動封裝成一個(gè)閉包(closure).

    比如我們有一個(gè)方法接受一個(gè)閉包參數(shù),當(dāng)閉包執(zhí)行結(jié)果為true的時(shí)候進(jìn)行打印:

    func logIfTrue(_ predicate: () -> Bool) {
         if predicate() {
             print("True")
         }
    }
    

    在調(diào)用的時(shí)候,我們會這樣寫:

    logIfTrue({return 2 > 1})
    

    swift中對閉包的寫法進(jìn)行了一些簡化,當(dāng)只有一條return語句的時(shí)候,我們可以這樣寫:

    logIfTrue({2 > 1})
    

    因?yàn)檫@個(gè)閉包是logIfTrue函數(shù)的最后一個(gè)參數(shù),也就是尾隨閉包,所以我們還可以簡寫如下:

    logIfTrue{2 > 1}
    

    猶豫簡寫的過多,這樣看起來其實(shí)已經(jīng)不那么好理解了,這時(shí)候我們就可以使用@autoclosure:

    func logIfTrue(_ predicate: @autoclosure () -> Bool) {
         if predicate() {
             print("True")
         }
    }
    

    這時(shí)候我們就可以直接寫:

    logIfTrue(2 > 1)
    

    他會自動把 2 > 1這個(gè)表達(dá)式轉(zhuǎn)換成 () -> Bool,這樣我么就得到一個(gè)寫法簡單,表意清除的式子.

    1. ??

    ?? 使用來快速判斷nil的.語意是:如果??操作符左邊的值是非nil的Optional值,就返回他的value,如果是nil,就有??操作符右邊的值代替,比如:

    var level: Int?
    var startLevel = 1
    
    var currentLevel = level ?? startLevel
    

    注意:@autoclosure并不支持帶有輸入?yún)?shù)的方法,也就是形如() -> T的參數(shù)才能使用這個(gè)特性進(jìn)行簡化.

    // ?? 的2中形式和底層實(shí)現(xiàn)
    func ??<T>(optional: T?, defaultValue: @autoclosure () -> T?) -> T?
    func ??<T>(optional: T?, defaultValue: @autoclosure () -> T) -> T
    // 我們之前用到的是第二種
    func ??<T>(optional: T?, defaultValue: @autoclosure () -> T) -> T {
         switch optinal {
             case .Some(let value):
                 return value
             case .None:
                 return defaultValue()
         }
    }
    

    有人可以會對為什么使用@autoclosure有疑問,說不使用閉包,直接賦值不好嗎? 原因是這樣的,這個(gè)默認(rèn)值可能是經(jīng)過特別復(fù)雜的計(jì)算獲得的,但是它只有在??左側(cè)為nil的時(shí)候才用的到,當(dāng)??左側(cè)非nil的時(shí)候我們根本用不到,所以就不用計(jì)算這個(gè)默認(rèn)值,我們通過這個(gè)閉包,就可以把默認(rèn)值的計(jì)算推遲到optional判定為nil之后,這就是巧妙之處.

    另外,swift中的&& 和||操作符其實(shí)也用到了@autoclosure

  4. Optional Chaining

    使用Optional Chaining可以讓我們拜托很多不必要的判斷和取值,但是使用的時(shí)候要小心,還是有坑的

    因?yàn)镺ptional Chaining是隨時(shí)都可能提前返回nil的,所以使用Optional Chaining所得到的東西其實(shí)都是Optional的,比如下邊一段代碼:

    class Toy {
        let name: String
        init(name: String) {
            self.name = name
        }
    }
    
    class Pet {
        var toy: Toy?
    }
    
    class Child {
        var pet: Pet?
    }
    

    在實(shí)際使用中,當(dāng)我們想要知道小明的寵物的玩具的名字的時(shí)候,可以通過Optional Chaining來拿到:

    let toyName = xiaoming.pet?.toy?.name
    

    雖然我們訪問的是name,并且Toy中name被定義為一個(gè)確定的String,而不是String?, 但我們拿到的toyName其實(shí)還是String?類型.因?yàn)樵贠ptional Chaining中隨時(shí)可能會遇到nil而提前返回,這個(gè)時(shí)候我們拿到的就是nil了.

    然而,實(shí)際使用中,我們通常會通過Optional Binding(可選綁定)來取值這樣的代碼:

    if let toyName = xiaoming.pet?.toy?.name {
         // 這時(shí)候拿到的toyName必然不是nil
    }
    

    單獨(dú)這樣看還是很清楚的,如果和其他的特性結(jié)合在一起,可能就會很麻煩了:

    // 給Toy添加一個(gè)extension
    // 當(dāng) child 的pet 有toy的時(shí)候就玩,沒有就不能玩
    extension Toy {
        func play() {
            // ...
        }
    }
    

    拿小明舉例子, 如果小明的pet有toy的話,就玩之:

    xiaoming.pet?toy?.play()
    

    如果現(xiàn)在除了小明還有小李小張等,我們需要把這一串調(diào)通抽象出來,做一個(gè)閉包方便使用.傳入一個(gè)child對象,于是我們就可能寫成下邊這個(gè)樣子:

    這是錯(cuò)誤的代碼:

    let playClosure = {(child: Child) -> () in
     child.pet?.toy?play()
    }
    

    這樣的代碼是沒有意義的!可能有人會問,問題處在那里呢?

    問題就在play()的調(diào)用上.定義的時(shí)候沒有寫play()的返回值,就是Void(同()是等價(jià)的),但是經(jīng)過前面的Optional Chaining以后,我們拿到的是一個(gè)Optional的結(jié)果,也就是我們最后得到的應(yīng)該是一個(gè)closure:

    let playClosure = {(child: Child) -> ()? in
     child.pet?.toy?play()
    }
    

    這樣調(diào)用的返回將是一個(gè)()?(等同于Void?),雖然看起來很奇怪,但這是事實(shí).使用的時(shí)候,我們通過Optional Binding來判定方法是否調(diào)用成功:

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

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

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