ReSwift 介紹

haipeng666 8年前發布 | 13K 次閱讀 iOS開發 移動開發

什么是 ReSwift

ReSwift 是基于 Redux 思想實現的 Swift 類庫。基本的流程如下

當用戶點擊了視圖上的某個元素時,會發出一個 Action ,這個 Action 包含了兩個基本元素: Action Type 和 Action Payload ,比如「點擊收藏按鈕」這個 Action ,可能會被描述為: Action("CollectButtonTapped", ["itemID": 189]) 。然后這個 Action 就會到達 Store , Store 也很簡單,只做兩件事:1. 接收 Action ;2. 將 Action 和 State 發送給 Reducer 。 Reducer 做的事情就更簡單了,接收 Store 發出的 Action 和 State ,內部運算之后,返回一個新的 State 。 Store 拿到了新的 State 后,再把 State 發送給 View 。 View 渲染新的 State 。

簡單描述下各個模塊的職責:

View

View 可以理解為一個「殼」,所有的數據都由 State 提供,這樣就把表現層和數據層分開了。

view = f(state)

Action

Action 用來描述發生了什么事情,比如不小心用腳踢到了椅子,神經系統就會把這個信息傳遞給大腦,這個信息就是 Action ,而大腦就是之后要講到的 Store 。

Store

這是核心模塊,就像大腦會不停地接受到各種 Action ,并作出反應,只不過在這里 Store 并不具備「做決定」的能力,而是把這個 Action 交給了所有可能關心它的 Reducers 。

ReSwift 推薦一個 App 只有一個 Store ,在實際情況中,如果這么做的話,會帶來不少的副作用,比如所有的模塊都需要依賴 Store ,這個 State 會很龐大,不可避免的會影響性能。所以,單個頁面或模塊有一個 Store 會比較合適。

State

State 是一個隱形的殺手,因為使用它極其方便,而它的危害也不會瞬間爆發,就像溫水煮青蛙一樣,等發現問題越來越多、被各種多線程問題困擾時,就會感受到它的威力了。

所以把 State 單獨拎出來,并且使用 Value Types 來解決各種多線程或變量被修改導致的問題。

WWDC 的 Protocol and Value Oriented Programming in UIKit Apps 中也推薦使用 Value Composition,而不是繼承,同時把 State 集中到一個地方處理,也有助于 Local Reasoning。

為什么要使用 ReSwift

確切說來是為什么要使用「單向數據流」的架構模式,主要有這么幾個好處:

  1. 數據單向流動容易讓結構變得清晰,出問題時也更容易排查。
  2. 使用了 「Value Types」作為流動的數據,避免各種詭異的「不小心被篡改」或多線程 bug。
  3. 在統一的入口處理數據(State),比起散落在各處更加容易控制。

Readme 里帶了一個簡單的 Demo,可以感受下。

源碼一瞥

ReSwift (3.0.0) 的源碼很精簡,對 Swift 熟悉的話,很快就能看完。說下我自己在看源碼的過程中學到的一些 tips 吧。

Reduce 的使用

reduce 在函數式編程的領域里會經常被用到,甚至可以實現 map / filter 等功能,足見其強大。它的運行規則是以函數的處理結果作為初始值,再結合數組中的元素返回處理結果,不斷循環,直到數組中的元素全部處理完成。

在 Swift 中,它是 Sequence 協議擴展的一個方法,簽名如下

public func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Self.Iterator.Element) throws -> Result) rethrows -> Result

在 ReSwift 中有好幾個地方都用到了 reduce ,比如通過它來達到 combineReducer 的效果

public struct CombinedReducer: AnyReducer {
      // self.reducers 包含了 AnyReducer 的實例
    public func _handleAction(action: Action, state: StateType?) -> StateType {
        return reducers.reduce(state) { (currentState, reducer) -> StateType in
            return reducer._handleAction(action: action, state: currentState)
        }!
    }
}

按照入隊列的先后,reducer 被依次執行,并且把生成的新的 State 作為下一個循環的初始值傳遞給下一個 reducer。

在處理 middleware 時,也有用到類似的技術,不過那個更加復雜些,涉及到 高階函數 。

裝飾器模式

裝飾器模式簡單來說就是在不改變類/方法原有功能的前提下,提供了一些額外的能力。比較常見的有 validator,客戶端提交的數據要入庫前需要做一下校驗,不通過的話直接返回。在 python 里裝飾器非常常見,比如在一個方法上加一個 @cached 或者 @validate 等 annotation。

在實現 Reducer 時,有用到這個模式:

public protocol AnyReducer {
    func _handleAction(action: Action, state: StateType?) -> StateType
}

public protocol Reducer: AnyReducer { associatedtype ReducerStateType

func handleAction(action: Action, state: ReducerStateType?) -> ReducerStateType

}

extension Reducer { public func _handleAction(action: Action, state: StateType?) -> StateType { return withSpecificTypes(action, state: state, function: handleAction) } }</code></pre>

_handleAction 對 handleAction 做了個校驗,( withSpecificTypes 函數里如果校驗不通過, handleAction 不會被執行),這樣對于使用者,只需繼承 Reducer 實現 handleAction 方法,ReSwift 內部調用時會使用 _handleAction 來做一些校驗。

在 StoreSubscriber 里也有用到類似的技術。

associatedtype 的使用

通過 associatedtype ,可以讓 protocol 使用 generic , Natasha 還寫過一篇關于 PAT 使用的文章 ,里面以寵物小精靈為例,通過 PAT 讓不同的小精靈具備了不同的能力。不過使用了 associatedtype 或 Self 后,就不能作為變量的類型來聲明了,比如 var something: AProtoclWithAssociatedType 這樣編譯器會報錯,具體原因可以參考 這篇文章 ,主要是因為無法指定 Generic 的類型,導致編譯器無法在編譯期間就確定具體的類型,對于強類型語言來說,這是不能接受的。

ReSwift 中,在定義 StoreType 時,有用到 associatedtype

public protocol StoreType {

associatedtype State: StateType

/// Initializes the store with a reducer and an intial state.
init(reducer: AnyReducer, state: State?)

//...

}</code></pre>

在定義 reducer protocol 時,也有用到(也是關聯了 StateType)。

對外只讀,對內可讀寫

在 OC 時代,通常的做法是在 .h 里聲明為 readonly ,然后在 .m 的 class extension 里,將同名的屬性聲明為 readwrite 。

Swift 沒有頭文件的概念,直接一句話搞定 private(set)

struct Subscription<State: StateType> {
    private(set) weak var subscriber: AnyStoreSubscriber? = nil
    let selector: ((State) -> Any)?
}

subscription 希望外部可以拿到 subscriber,但不要修改它,于是在前面加了 private(set) ,也就是把 set 方法標記為 private。

小結

ReSwift 還是挺值的一試的,一方面是因為單向數據流確實對程序的清晰度有幫助,另一方面 ReSwift 的代碼很簡潔,內部實現比較容易搞明白,這樣即使出問題也比較容易定位。 Realm 上有作者分享的案例,可以參考下。不足嘛肯定也有,比如功能比較簡單,只是做了數據流,缺少 Diff 支持,在做列表更新/刪除時會比較痛苦;如何與 MVVM 等比較成熟的架構有效地結合起來等。

除此之外,由于數據都通過 State 來傳遞,可以在出 bug 時,上傳當時的 state 內容方便定位;還可以基于 State 來做 時光機 。不妨在 Side Project 中嘗試下。

 

 

來自:http://limboy.me/tech/2016/12/04/reswift-analyze.html

 

 本文由用戶 haipeng666 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!