Rx可讓你的應用處於一個聲明的方式來使用!html
Bindings :綁定react
1.能夠很是輕鬆的將兩個textfiled的text綁定起來。也就是按照必定的意願組合起來git
Observable.combineLatest(firstName.rx_text, lastName.rx_text) { $0 + " " + $1 } .map { "Greetings, \($0)" } .bindTo(greetingLabel.rx_text)
分析:1.combineLatest 是將firstName以及lastName的內容組合起來而後在中間添加一個空格 github
2.map 遍歷剛剛組合出來的字符串 前面添加Greetings編程
3.而後跟greetingLabel的text綁定起來 swift
總結:也就是說上面這幾行代碼就能夠將firstName以及lastName經過組合外加變化的方式賦值給greetingLabel服務器
2.一樣的UITableview以及UICollectionView也能夠作相似的處理網絡
viewModel .rows .bindTo(resultsTableView.rx_itemsWithCellIdentifier("WikipediaSearchCell", cellType: WikipediaSearchCell.self)) { (_, viewModel, cell) in cell.title = viewModel.title cell.url = viewModel.url } .addDisposableTo(disposeBag)
分析 : 1.將ViewModel的每一行都綁定一個WikipediaSearchCell閉包
2.在閉包中返回了tableview,cell,以及Viewmodel,能夠進行相應的操做併發
3.addDisposableTo當全部者須要釋放時,取消監聽
總結 :若是你據說過MVVM模式的話,函數式編程也就是上面那種模式將會實現你想要的
官方建議老是使用.addDisposableTo(disposeBag),
雖然這個在簡單的綁定中沒有必要。
Retries :重試
若是API不會失敗那麼將是美妙的。可是很不幸的是他們常常失敗,下面讓咱們來看看這個API方法
func doSomethingIncredible(forWho: String) throws -> IncredibleThing
若是你使用這個函數像之前同樣。那麼它將會很可貴去重試當它失敗的時候。更不用說複雜的模型exponential backoffs。固然那頗有可能。可是這個代碼可能包括不少過渡的狀態是你並不關心的,而且那些也可有可無。
思想上,你想要去抓住這些本質的東西,而後重試。而且可使用這些到任何一個操做上面
那麼使用Rx將會很是簡單
doSomethingIncredible("me") .retry(3)
你能夠簡單的創造自定義的重試操做
Delegates : 代理
中止使用這些單調乏味無實際意義的方法吧,例如:
public func scrollViewDidScroll(scrollView: UIScrollView) { [weak self] // what scroll view is this bound to? self?.leftPositionConstraint.constant = scrollView.contentOffset.x }
使用
self.resultsTableView .rx_contentOffset .map { $0.x } .bindTo(self.leftPositionConstraint.rx_constant)
KVO :Key-Value-Observer
中止使用 :當鍵值觀察者仍然在註冊中而'TickTock'已經銷燬時,觀察的信息將會被泄漏,甚至也許會錯誤的對應到其餘的對象上面去
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
使用rx_observe 和 rx_observeWeakly
view.rx_observe(CGRect.self, "frame") .subscribeNext { frame in print("Got new frame \(frame)") }
someSuspiciousViewController .rx_observeWeakly(Bool.self, "behavingOk") .subscribeNext { behavingOk in print("Cats can purr? \(behavingOk)") }
Notifications :通知
中止使用
@available(iOS 4.0, *) public func addObserverForName(name: String?, object obj: AnyObject?, queue: NSOperationQueue?, usingBlock block: (NSNotification) -> Void) -> NSObjectProtocol
而是採用
NSNotificationCenter.defaultCenter() .rx_notification(UITextViewTextDidBeginEditingNotification, object: myTextView) .map { /*do something with data*/ } ....
過渡狀態 :
這裏也有不少問題關於過渡狀態的當使用異步編程時。一個很是典型的例子就是一個自動完成的搜索框
若是你之前寫自動完成代碼沒有使用Rx,那麼出現的第一個問題須要被解決的就是當ab的搜索的網絡請求正在進行,c又輸入了。那麼這個請求應該被取消。OK,那也不會很是難去解決。你也許僅用一個額外的變量來控制這個即將到來的請求就能夠了。
接下來一個問題是若是這個請求失敗了。你須要去作麻煩的重試邏輯。一對或者多個能夠捕獲重試請求來清除,這個問題但是能夠被解決
若是編碼可以現實等待一段時間再去發送請求到服務器那將是極好的。畢竟,咱們不想去浪費咱們的服務以防有些人正在碼很長的字符。一段額外的計時器字段也許須要
這裏仍然有一個問題,那就是查詢正在執行須要顯示出來,並且那將會被展現以防咱們失敗即便咱們嘗試了不少次。
把上面這些問題碼出來而且進行適當的測試將會很是繁瑣。這裏有一段同樣邏輯的代碼是用Rx寫的
searchTextField.rx_text .throttle(0.3, scheduler: MainScheduler.instance) .distinctUntilChanged() .flatMapLatest { query in API.getSearchResults(query) .retry(3) .startWith([]) // clears results on new search term .catchErrorJustReturn([]) } .subscribeNext { results in // bind to ui }
這裏不須要其餘額外的標誌或者字段。Rx將會處理全部這些過渡的混亂的狀態
Compositional disposal
讓咱們假設有這麼一個狀況:你須要展現模糊的圖像在tableview上面。首先這些圖片須要從URL上面獲取下來,而後編碼,而後模糊化
若是一個cell已經存在了可視的tableview裏面 那麼 整過過程會被取消將會是極好的。畢竟帶寬和模糊的進程耗時是很是昂貴的
一旦一個cell進入可視的範圍後不會立馬請求圖片也是極好的,由於用戶劃的很是快的話。那麼將會有不少請求被髮送和取消。
限制併發的圖片操做的數量也將會是很好的,畢竟模糊圖片是一個昂貴的操做
所以咱們可使用Rx:
// this is a conceptual solution let imageSubscription = imageURLs .throttle(0.2, scheduler: MainScheduler.instance) .flatMapLatest { imageURL in API.fetchImage(imageURL) } .observeOn(operationScheduler) .map { imageData in return decodeAndBlurImage(imageData) } .observeOn(MainScheduler.instance) .subscribeNext { blurredImage in imageView.image = blurredImage } .addDisposableTo(reuseDisposeBag)
這段代碼能夠作到上面全部的一塊兒。當imageSubscription被取消了。那麼它將取消全部依賴的異步的操做。確保沒有流氓的圖片被綁定到UI上面
Aggregating network requests
你是否須要發送兩個請求而後合成結果當他們都完成以後呢?
是的,這就是Zip 操做
let userRequest: Observable<User> = API.getUser("me") let friendsRequest: Observable<Friends> = API.getFriends("me") Observable.zip(userRequest, friendsRequest) { user, friends in return (user, friends) } .subscribeNext { user, friends in // bind them to the user interface }
那麼若是APIs返回的數據在子線程,可是綁定卻須要發送在主線程呢。observeOn將能夠實現
let userRequest: Observable<User> = API.getUser("me") let friendsRequest: Observable<[Friend]> = API.getFriends("me") Observable.zip(userRequest, friendsRequest) { user, friends in return (user, friends) } .observeOn(MainScheduler.instance) .subscribeNext { user, friends in // bind them to the user interface }
這裏還有更多的實用例子是Rx的閃光點
State
容許多態改變的語言容易得到全局狀態而且改變他。可是不可控的全局狀態的變化將很容易致使組合爆炸combinatorial explosion.
可是另外一方面。當它被一種聰明的方式使用時,專橫的語言將將會以一種更高效的方法編碼已更接近與硬件
和組合爆炸作鬥爭經常使用的方式就是保持狀態越簡單越好。使用 unidirectional data flows 數據導出模型
這就是Rx的閃光點
Rx函數式專橫世界裏面的甜點。它使您可以使用不可變的定義和純函數的可變狀態的進程快照可靠的組合方式。
Easy integration
如何你才能建立你本身的觀察者呢。那將會很是簡單。下面的代碼來自RxCocoa。你所須要作的就是去除HTTP請求使用NSURLSession
extension NSURLSession { public func rx_response(request: NSURLRequest) -> Observable<(NSData, NSURLResponse)> { return Observable.create { observer in let task = self.dataTaskWithRequest(request) { (data, response, error) in guard let response = response, data = data else { observer.on(.Error(error ?? RxCocoaURLError.Unknown)) return } guard let httpResponse = response as? NSHTTPURLResponse else { observer.on(.Error(RxCocoaURLError.NonHTTPResponse(response: response))) return } observer.on(.Next(data, httpResponse)) observer.on(.Completed) } task.resume() return AnonymousDisposable { task.cancel() } } } }
Benefits :
簡而言之,使用Rx將會使你的代碼:
1.可組合 由於Rx就是可組合的暱稱
2.可複用 由於Rx可組合
3.聲明性 由於定義是不可改變的。僅僅數據改變
4.可理解而且精確 提升了抽象的水平而且移除了過渡狀態
5.穩定性 由於Rx 代碼是能夠經過單元測試的
6.更少的狀態性 由於你的模型化應用時單向的數據流向
7.沒有內存泄漏 由於資源管理很是簡單
It's not all or nothing
在你的應用中儘量多的使用Rx那將是不錯的注意
可是若是你不知道全部的操做符。不管那裏是否存在一些操做符恰好符合你的特定的需求
固然全部的Rx操做符都是基於數學和直觀的
好消息是大概10-15個操做符包含了全部的使用情形
這裏有一列表Rx操做符 all Rx operators 和一大羣 被RxSwift支持的操做符currently supported RxSwift operators.
若是你所須要的操做符不在列表中。那麼創造你本身的操做符吧。
若是你在自定義操做符中遇到任何問題。來這裏試試看吧 jump out of Rx monads