深刻理解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,则不会,减小了性能消耗。

相关文章
相关标签/搜索