近來在用 Swift 開發(fā) App 的過程中,最大的心得就是:我開始漸漸用「Swift 思維」來思考了。回顧剛開始我用 Swift 時,只是套用它的語法而已,腦子里依然是 Objective-C 思維。 這段時間,隨著對 Swift 基本特性的掌握,我開始有意識地學習并嘗試一些 Swift 才有的特性,此謂「Swift 思維」。Swift 有很多專有(Objective-C 沒有的)的模式,今天我就從一個很簡單的例子講起,那就是: 如何用 Swift 思維設(shè)計網(wǎng)絡(luò)請求。
做過網(wǎng)絡(luò)類應用的同學應該都知道,我們做一個網(wǎng)絡(luò)請求時,通常會有兩個結(jié)果:一個是失敗,返回錯誤,一個是成功,返回結(jié)果。當然途中還會有更復雜的情況,比如:1、網(wǎng)絡(luò)請求本身的失?。ū热缇W(wǎng)絡(luò)超時);2、API 端返回的內(nèi)部結(jié)果型失?。ū热缑艽a不正確)。這里我們就不細分了。 在傳統(tǒng)的 Objective-C 的項目里,有幾種處理這種異常的情況,主要是: 1、直接判斷 NSError NSError *error = nil; id result = [API doSomething:&error]; if (error != nil) { NSLog("Oh Error!") } 這種處理太直白,一般會阻塞當前的線程,因而不推薦。一般用的比較多的是下面兩種: 2、通過 success,failture 的 block 來處理 [API doSomethingWithSuccess:^(id result) { NSLog(@"Seems good") } failure:^(NSError *error) { NSLog(@"Oh Error!") }]; 這種就相對好點了,通過 Block 及內(nèi)部實現(xiàn),可以做到不阻塞當前線程,在有結(jié)果的時候再進行處理。在對應的 Block 處理對應的情況:成功 or 失敗。 不過這個設(shè)計依然還有一點缺陷,因為它對結(jié)果的處理分散在不同的 Block,如果我需要統(tǒng)一處理無論成功或失敗的情況,那么需要分別調(diào)用,不是太直觀了。所以,我們還有第三種模式。 3、通過統(tǒng)一的 completionHander 的 block 來處理 [API doSomethingWithCompletionHandler:^(id result, NSError *error) { if result != nil { NSLog(@"Seems good") } if error != nil { NSLog(@"Oh Error!") } }]; 這種通過 CompletionHandler 來統(tǒng)一作成功結(jié)果和錯誤失敗的處理應該是現(xiàn)在設(shè)計的首先,包括系統(tǒng)自己的 API 也是這樣設(shè)計的。特別適合在一個 Block 里就把所有情況處理掉的需求。 Swift 式網(wǎng)絡(luò)請求處理 簡單列舉了三種 Objective-C 下常見的網(wǎng)絡(luò)請求類處理方式,看起來還不錯,那么 Swift 模式是什么樣的,能做好更好嗎?我覺得是的。 Swift 里有著非常棒的 enum 機制,所有的枚舉情況不但可以是任何類型,而且可以是不一樣的類型。這意味著,我們在 Swift 里可以包裝一種結(jié)果型 enum,比如: enum Result { case Error(NSError) case Value(JSON) init(_ e: NSError, _ v: JSON) { if let ex = e { self = Result.Error(ex) } else { self = Result.Value(v) } } } 這段是我真實世界的代碼,用在了我的微博客戶端里。 代碼很簡單,我定義了一個名為「Result」的 enum 對象,它會包裝兩種情況,一種是 Value,在網(wǎng)絡(luò)請求成功時,它就是一個 JSON 值;第二種時 Error,是一個 NSError 值,在網(wǎng)絡(luò)請求失敗時,包含著具體的錯誤信息。 這樣,就成功地把一個網(wǎng)絡(luò)請求下的可能的兩種情況包裝在了一個 Result 對象里,這個對象,要么是成功的結(jié)果,要么就是失敗的錯誤,永遠不會有同時有結(jié)果和錯誤。于是,我們的網(wǎng)絡(luò)請求處理代碼可以更為簡單的設(shè)計成這樣: API.doSomethingWithCompletionHandler({ (result) -> Void in switch (result) { case let .Error(e): NSLog("Oh Error!") case let .Value(json): NSLog("Seems good") } }) 看起來似乎和前面 Objective-C 的第二種模式一樣?似乎又像第三種?估且稱之為混合模式吧。讓我來簡單說說這種模式有什么好處: 首先,我們通過 Switch 條件判斷這種非此即彼的模式,我們可以減少很多錯誤的發(fā)生,保證條件分支判斷不會出問題;其次,我們依然只是在一個 Closure (這里換成 Swift 術(shù)語,而不是 Objective-C 的 Block)處理我們的一個請求結(jié)果 result,因而可以在前前后后做些其他對結(jié)果的統(tǒng)一處理,保證我們邏輯的統(tǒng)一性。 這就是我所認為的 Swift 這種模式的好處了。 通過這種模式改造我的項目后,我覺得代碼變得更整潔、邏輯清晰,也不會有遺失的錯誤處理情況。當然我做采用的還只是簡化版的 Swift 模式網(wǎng)絡(luò)處理,更為強大的例子大家可以參考 swiftz 項目的 Result 對象,它使用了 Swift 的 Generic 特性,從而使其可以包裝任意值(不僅僅是 JSON),從而大大增強了擴展性:https://github.com/typelift/swiftz/blob/master/swiftz_core/swiftz_core/Result.swift#L12 我依然還在用 Swift 思維改造項目的過程中,目前這種模式應該還是有改進余地的,希望能和大家做更多有關(guān)這個的討論與交流。
Tag: Swift.
<推廣> Manico 是一個專門為 OS X 高效率人士設(shè)計的 App 啟動與切換工具,使用它將加倍電腦日常使用的效率。 You can leave a comment. |
|