Swift項目初體驗瑣記(1)

雖說從Swift剛出生就一直有關注,但是由于ABI不穩定以及第三方支持還不夠好等等原因,一直沒有用于實際的真實項目中,直到3.0之后感覺時機成熟了,剛好最近有一個比較小的項目,于是有了一次實際項目的初體驗,也順便寫下一些總結。(總體內容會比較淺顯,慚愧了

Swift與Objective-C常見寫法差異

  1. 判斷字符串相等、==與===的區別
    在oc中,我們已經習慣了使用isEqualToString方法來判斷兩個字符串是否相等,似乎也不覺得有什么問題,這是因為oc使用==來統一引用的比較與值類型的比較,但是NSString在內存模型上比較特殊,以至于oc中使用了一個專門的比較方法,而在其他許多語言中只需要用==對比字符串就好。Swift改進了這一點,順帶一提,Swift中使用==來比較值類型是否相等,而使用===來判斷引用類型是否相等。

  2. 數組遍歷
    在oc中,我喜歡使用enumerateObjectsUsingBlock方法遍歷數組并進行操作,大概是這個樣子的:

    NSArray *list = @[@1,@2,@3,@4,@5];
    [list enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"obj = %@, idx = %d", obj , (int)idx);
    }];
    

    但是Swift自然不會還存在如此oc模樣的方法,相替代的,Swift可以用foreach方法:

    let list = [1,2,3,4,5]
    list.forEach { number in
        print(number)
    }
    

    但是如果我們想要像oc一樣同時獲取元素和下標的話,就要換一種寫法了:

    let list = [1,2,3,4,5]
    for (idx, number) in list.enumerated() {
        print("idx is \(idx) and number is \(number)")
    }
    

    無論如何,至少看起來比oc是要簡潔多了。

    另外,Swift的一個令人激動的特色就是內嵌一些函數式編程范式,因此如果我們要對數組逐個進行操作的話用map方法是再方便不過。配合上一些語法糖,非常之簡單而且直觀,比如我們把上面的list的每個元素乘以2:

    print(list.map { $0 * 2 })
    

    還有更多的基本用法如filter和reduce等等這里就不贅述了,很多相關教程

  3. 判斷或獲取類名字符串
    在oc中,獲取一個對象的類型是很簡單的,只需要調用class類型方法就好,比如[AClass class],如果判斷某個對象是否屬于這個類型,則使用isKindOfClass方法:[self isKindOfClass:[AClass class]],如果需要獲取一個實例類名的字符串,則NSStringFromClass([AClass class])。而在Swift中,獲取一個類的class很簡單,只需要AClass.self就可以了,但是,如果你想獲取一個實例對象的類名字符串,則需要這樣寫String(describing: type(of: aInstance)),額,總覺得有點奇怪。

Swift特性的一些實踐經驗總結

  1. 要說在Swift中最有特色體驗最明顯的,應該算可選類型了,誠然,可選類型可以幫助我們構建更安全更可靠的app,但是毫無疑問,可選類型中各種轉換和拆包讓人覺得略煩,特別是我這樣的新手,稍不注意就是報錯或者沒有得到符合預期的值。然后在stackoverflow上看相關的討論的時候,發現一個很有趣的小方法:

    extension Optional {
        func valueOrDefault<T>(defaultValue: T) -> T {
            switch(self) {
            case .none:
                return defaultValue
            case .some(let value):
                return value as! T
            }
        }
    }
    

    這是一個Optional類的擴展,用了泛型,代碼就不解釋了,很清晰。用上這個類別之后,我們可以方便的、安全的寫這樣的代碼:

    var a: Int?
    //a = 1
    let b = a.valueOrDefault(defaultValue: 2)
    print("b = \(b)")
    

    在不知道a是否有確切值的情況下,就可以將a安全的賦值給b,通過提供默認值的方式,簡直贊。在這個例子中,如果把注釋的那行取消注釋,b則為1,在大量使用可選類型的基礎上,使用這個小技巧可以避免使用一堆堆的諸如if let b = a as? Int的代碼。

  2. Swift與強大的枚舉
    在oc或者說c中,枚舉的用途也許僅僅是一個同數據類型的常量集合,但是在Swift中,枚舉的能力被大大擴充,甚至完全可以替代簡單的class。而且在一些方面的最佳實踐中,枚舉的出場率也相當高,比如notification.name。先不說有沒有必要這樣做,在我的實踐中,發現Swift的枚舉確實是一個很方便而且簡化邏輯的玩意兒,還是舉個栗子吧,比如我們有一個item,包含一個有3種狀態的state變量,我們還需要根據這個state輸出對應的顏色和說明文字等:

    struct Item {
        enum ItemState {
            case success
            case fail
            case ready
            
            //inner function
            func showColor()->UIColor {
                switch self {
                    case .success: return UIColor.green
                    case .fail: return UIColor.red
                    case .ready: return UIColor.gray
                }
            }
            
            func descStr()->String {
                return "[output: \(self)]"
            }
        }
        var name = "example"
        var state = ItemState.ready
    }
    

    直接在enum中寫function有什么好處呢?可以免去很多的判斷,從而直接綁定輸出,同時也減少了出錯的機會,在這個例子中,我們可以如下調用:

    var item = Item()
    print("now state is = \(item.state.descStr()) , color is \(item.state.showColor())")
        
    item.state = .success
    print("now state is = \(item.state.descStr()) , color is \(item.state.showColor())")
        
    item.state = .fail
    print("now state is = \(item.state.descStr()) , color is \(item.state.showColor())")
    

    我們可以任意改變state,然后輸出當前state對應的各種綁定變量,把邏輯內聚在enum中,外部的調用會非常順暢。
    當然,這其實算是最基礎的用法了,關于enum還有更多高級且高端的用法,可以參考Swift 中枚舉高級用法及實踐

  3. Swift中的反射
    從現狀來看,Swift的動態性似乎是不如Objective-C的,某些諸如方法交換和動態綁定似乎底層用的也還是Objective-C的runtime。但是好在Swift還是有一個反射的替代方案,比較常用的一個地方就是,在需要保存一個NSObject對象到本地時,必須實現coder的相關方法,默認的做法是需要你把class內每個屬性都分別寫一個轉換,非常麻煩而且容易出錯,在Objective-C中可以通過運行時獲取所有成員變量和類型來做這個事情,相對應的,Swift可以用Mirror:

    convenience required init?(coder aDecoder: NSCoder) {
        self.init()
    
        for child in Mirror(reflecting: self).children {
            if let key = child.label {
                setValue(aDecoder.decodeObject(forKey: key), forKey: key)
            }
        }
    }
    
    func encode(with aCoder: NSCoder) {
        for child in Mirror(reflecting: self).children {
            if let key = child.label {
                aCoder.encode(value(forKey: key), forKey: key)
            }
        }
    }
    

    還是比較方便快捷的,我們可以再優化一下,把這個放進NSObject的extension里,這樣所有需要序列化的object就可以不用再繁復地一個個寫了 :)

下一篇我會總結一下目前用到的常用第三方框架和組合用法。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容