深刻理解RxSwift

簡介

本篇重點在於深刻RxSwift的部分經常使用特性,因此但願讀者在瞭解RxSwift官方的基本講解與Demo以後再進行閱讀。swift

RxSwift版本爲5.0.0以上。緩存

Dispose

在寫代碼的時候,咱們常常會在不少狀況下建立一個Observable數據結構

_ = Observable<String>.create { observerOfString -> Disposable in
    observerOfString.on(.next("😬"))
    observerOfString.on(.completed)
    return Disposables.create()
}
複製代碼

又或者是在監聽Observable閉包

let disposeBag = DisposeBag()

Observable<Int>.empty()
    .subscribe { event in
        print(event)
    }
    .disposed(by: disposeBag)
複製代碼

這裏老是看到是DisposablesDisposeBag,那麼它們究竟是什麼。併發

Disposables

Disposables是一個簡單的結構體socket

public struct Disposables {
    private init() {}
}
複製代碼

經過extension生成create方法, 建立四種不一樣的Disposable。性能

Disposable定義:優化

public protocol Disposable {
    /// Dispose resource.
    func dispose()
}
複製代碼

Cancelable, 在Disposable的基礎上添加了isDisposed屬性,便於追蹤Disposable的狀態:ui

public protocol Cancelable : Disposable {
    /// Was resource disposed.
    var isDisposed: Bool { get }
}
複製代碼
  1. NopDisposable, 在disposal時什麼都不作。
  2. AnonymousDisposable, 建立時傳入一個public typealias DisposeAction = () -> Void類型的閉包,在disposal時調用。
  3. BinaryDisposable, 在建立時傳入兩個Disposable類型的參數,在disposal時調用。
  4. CompositeDisposable, 在建立時傳入多個Disposable類型的參數,在disposal時調用。

除了這四種能夠經過快速建立的,還有其餘類型的Disposablespa

  • BooleanDisposable,主要用於追蹤disposal的狀態,初始化時傳入Bool類型的參數,表示是否已經被Dispose。
  • SubscriptionDisposable,主要在Subject中使用。
  • RefCountDisposable,初始化時傳入一個Disposable類型的參數,如同命名,內存採用了引用計數的管理方法,調用retain方式時計數+1,調用release時計數-1,引用計數不能小於0,當等於0時,若是沒有調用過dispose方法(實際上是將內部的_primaryDisposed屬性標記爲true),也不會觸發dispose方法,一樣若是引用計數不爲0,調用dispose方法也不會觸發內部的Disposable的dispose。
  • ScheduledDisposable,初始化時傳入一個Disposable類型的參數與一個ImmediateSchedulerType,指定傳入的Disposable在某個線程上執行dispose。
  • SerialDisposable: 能夠手動替換內部的Disposable,若是在_isDisposed爲false的狀態,替換後會自動觸發以前的Disposable的dispose方法,若是爲true,則直接觸發替換的Disposable的dispose方法。
  • SingleAssignmentDisposable:對內部的Disposable只能設定一次,若設定屢次會報錯,在沒有設定的狀況下觸發dispose方法也會報錯。

DisposeBag

DisposeBag如同名字,是一個Bag類型的數據結構,裏面存放Disposable的數據。

每次看到Disposable類型調用disposed(by: disposeBag),實際上是將Disposable放在DisposeBag中進行管理,當DisposeBag進行dispose時,對其中管理的Disposable分別dispose,可是DisposeBag不能主動調用,只能在deinit時自動釋放,因此想要進行dispose操做,只能將disposeBag從新賦值,好比:

disposeBag = DisposeBag()
複製代碼

Subject

簡介

Subject是在RX的某些實現中可用的橋接或代理。

經過繼承了ObserverType,能夠成爲一個觀察者,能夠經過on(_ event: Event<Element>)發送事件。

經過繼承了Observable,能夠成爲一個被觀察者,能夠經過subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element推送事件。

Subject都是經過Broadcasts的方式傳播事件。

冷熱信號

在RAC中經過Signal與SignalProducer區分冷熱信號的概念,RxSwift其實也有冷熱信號。

冷信號是指在被觀察以後纔會推送事件。

熱型號是指就算沒有被觀察,也能推送事件。

能夠理解爲一個主動一個被動。

這是一個比較抽象的例子:

let subject = Subject()
subject.onNext("1")
subject.subcribeNext { value in
    print(value)
}
subject.onNext("2")
若是是冷信號,這時候會打印1 2,熱信號則只會打印2.
複製代碼

各類Subject

在Rx中有四種Subject。分別是BehaviorSubjectPublishSubjectReplaySubjectAsyncSubject

下列圖標中,圓表明next信號,豎線表示complete,x表示error。

PublishSubject就是一個熱信號的Subject。

PublishSubject
PublishSubject

BehaviorSubject是一個有初始值,緩存數量爲1的Subject。在5.0版本以前的RxSwift中,有一個叫作Variable的屬性,在5.0以後,由於相同的特性,徹底被BehaviorSubject取代。

BehaviorSubject
BehaviorSubject

ReplaySubject是一個能夠自定義緩存數量的Subject。當設定緩存數量爲0時,幾乎能夠當作PublishSubject使用,緩存數量爲1時,能夠當作一個沒有初始值的BehaviorSubject使用。

ReplaySubject

AsyncSubject,只會在接收到Complete事件後,纔會Subscribe最後一個信號,若是沒有在onComplete以前沒有onNext事件,或者觸發了onError,則不會觸發Subscribe。

AsyncSubject
AsyncSubject

Demo

簡單的按鈕處理,當用戶名長度大於4位且密碼長度大於6位時,登陸按鈕才能點擊:

private var disposeBag = DisposeBag()
private let nameSubject = BehaviorSubject<String>(value: "")
private let passwordSubject = BehaviorSubject<String>(value: "")
private let loginSubject = BehaviorSubject<Bool>(value: false)
private let nameTextField = UITextField()
private let passwordTextField = UITextField()
private let loginButton = UIButton()
......
//中間內容省略
......
nameTextField.rx.text.orEmpty.bind(to: nameSubject).disposed(by: disposeBag)
passwordTextField.rx.text.orEmpty.bind(to: passwordSubject).disposed(by: disposeBag)
Observable<Bool>.combineLatest(nameSubject, passwordSubject) { (name, password) -> Bool in
    return name.count > 4 && password.count > 6
}.bind(to: loginButton.rx.isEnabled).disposed(by: disposeBag)
複製代碼

經過某個實時持續的交互,每秒刷新UI:

private let socketSubject = ReplaySubject<String>.create(bufferSize: 1)
......
DoSomething { 
    socketSubject.onNext(value)
}

Observable<Int>
    .interval(.seconds(1), scheduler: MainScheduler.instance)
    .withLatestFrom(socketSubject)
    .distinctUntilChanged()
    .subscribe(onNext: { (value) in
        print(value)
    }).disposed(by: disposeBag)
複製代碼

思考

若是ReplaySubject的bufferSize爲1,是否與BehaviorSubject相同?

若是ReplaySubject的bufferSize爲0,是否與PublishSubject相同?

Schedulers

Schedulers是RxSwift中的調度機制。

主要運算符只有兩個observeOn以及subscribeOn

sequence1
  .observeOn(backgroundScheduler)
  .map { n in
      print("This is performed on the background scheduler")
  }
  .observeOn(MainScheduler.instance)
  .map { n in
      print("This is performed on the main scheduler")
  }
  .subscribeOn(subscribeScheduler)
  .subscribe { _ in
      print("This is performed on the subscribeScheduler")
    }
複製代碼

經過observeOnsubscribeOn能夠控制處理信號的線程,而且能夠支持屢次切換。

各類Scheduler

  • CurrentThreadScheduler(串行)。指代當前線程,若沒有指定Schuduler,則默認使用。
  • MainScheduler(串行)。主線程,一般使用進行UI操做。在subscribeOn時更應該用作過優化的ConcurrentMainScheduler
  • SerialDispatchQueueScheduler(串行)。串行線程,主線程也是一種串行線程。
  • ConcurrentDispatchQueueScheduler(併發)。在初始化時也能夠傳入串行dispatch queue,也不會有任何問題。適用於須要在後臺的工做。
  • OperationQueueScheduler(併發)。是NSOperationQueue的一種抽象示例。適用與單個任務量大的任務並行處理的狀況,同時但願設定maxConcurrentOperationCount

思考

  1. 爲何說在subscribeOn時更應該用作過優化的ConcurrentMainScheduler
  2. 在閱讀文檔時,常常看到在observeOn時,若是指定了使用SerialDispatchQueueScheduler,會有所優化,具體是怎麼優化的?

對於第二個問題,在閱讀代碼時,發如今observeOn時作了單獨的處理

public func observeOn(_ scheduler: ImmediateSchedulerType)
    -> Observable<Element> {
        if let scheduler = scheduler as? SerialDispatchQueueScheduler {
            return ObserveOnSerialDispatchQueue(source: self.asObservable(), scheduler: scheduler)
        }
        else {
            return ObserveOn(source: self.asObservable(), scheduler: scheduler)
        }
}
複製代碼

對於非SerialDispatchQueueScheduler,會經過將事件以隊列的方式以及加遞歸鎖的方式存儲。 而SerialDispatchQueueScheduler,則不會,減小了性能消耗。

相關文章
相關標籤/搜索