简单聊聊RXSwift数据绑定

3,041 阅读8分钟

叨叨两句

最近又重学RxSwift,产生了一些心得体会,本意想分享出来避免初学者走弯路,也希望获得一些大佬的指点,让自己进步能够快一些,谁知道随着学习的深入,也越来越发现这里面的趣味性,所以就变成了一篇很长的博文。

略过千篇一律的使用教程,进入我的学习世界吧。。。

正文

a.可监听序列与观察者的关系

1.可监听序列绑定观察者

2.观察者需要在可监听序列Observable的subscribe方法后面描述内容,subscribe就是观察者的内部过程。

3.观察者就是由后面的 onNextonErroronCompleted的这些闭包构建出来的一系列操作,包括观察后的响应和失败,以及观察完成。

b.观察者特征

观察者有两种:AnyObserver、Binder;Binder是UI专用观察者只负责next部分

由前面的内容我们知道,subscribe的内部描述就是观察者的整个过程。而我们已知的Rx观察者,其实是基于subscribe实现过程的封装

创建一个Int序列

let disposeBag = DisposeBag()
let number = Observable<Int>.create { (observable) -> Disposable in
  observable.onNext(555)
  return Disposables.create()
}

代码一

number.subscribe { (element) in
  print("this is element: (element).")
} onError: { (error) in
  print("this is error: (error).")
} onCompleted: {
  print("this is completed.")
}.disposed(by: disposeBag)

代码二

let observer = AnyObserver<Int> { (event) in
  switch event {
  case .next(let element):
    print("this is element: (element).")
  case .error(let error):
    print("this is error: (error).")
  case .completed:
    print("this is completed.")
​
  }
}
number.subscribe(observer).disposed(by: disposeBag)

subscribe被封装成Observer类型;也就是说,代码一等于代码二

Binder也好理解,它具备两个特征:不处理错误,在主线程监听。写法也很简单

let observer = Binder<CGFloat>(label) { (view, number) in
  view.alpha = number
}
number.subscribe(observer).disposed(by: disposeBag)

在UI观察者中,它的内部过程可以用Binder封装起来,或者用RxCocoa准备好的扩展用法,因为UI特征的观察者,没有错误事件,这种特性无疑简化了subscribe内部过程

c.序列特征

序列分为默认序列和特征序列

1.Observable默认序列

create(_ subscribe: @escaping (AnyObserver<Element>) -> Disposable) -> Observable<Element>

可以看到subscribe逃逸闭包中有一个AnyObserver,这说明什么。

说明我们需要实现的观察者类型是AnyObserver,它可能发出元素、错误和成功回调

2.以下都是特征序列

(1)single:

create(subscribe: @escaping (@escaping SingleObserver) -> Disposable) -> Single<Element>

从上面的代码,可以看到在subscribe嵌套的逃逸闭包中,有一个SingleObserver,这是一个关键字修饰的闭包

typealias SingleObserver = (SingleEvent<Element>) -> Void

最后我们得到一个SingleEvent的枚举

public enum SingleEvent<Element> {
    case success(Element)
    case error(Swift.Error)
}

也因此,可以根据上述的内容,得到一个结论:Single会发出一个元素,或者发出0个元素(发出一个错误),它的枚举值是SingleEvent。

所以,它的使用场景,可以用于具备成功或者错误的回调中。

(2)completable:

create(subscribe: @escaping (@escaping CompletableObserver) -> Disposable) -> PrimitiveSequence<Trait, Element>

根据上面的推断,可以很轻易的通过CompletableObserver得出CompletableEvent枚举

typealias CompletableObserver = (CompletableEvent) -> Void
public enum CompletableEvent {
    case error(Swift.Error)
    case completed
}

也因此,结论就是:Completable发出0个元素,并且产生一个错误事件或者完成事件,它的枚举值是CompletableEvent。

它的使用场景,可以用于关心任务的完成状态,不关心中间结果。

(3)maybe:

create(subscribe: @escaping (@escaping MaybeObserver) -> Disposable) -> PrimitiveSequence<Trait, Element>

依然参照前面的推断,可以很轻易的通过MaybeObserver得出MaybeEvent枚举

typealias MaybeObserver = (MaybeEvent<Element>) -> Void
public enum MaybeEvent<Element> {
    case success(Element)
    case error(Swift.Error)
    case completed
}

Maybe会从三种,任意发出一种

窥探Single、Completed、Maybe

a.定义

Single

public typealias Single<Element> = PrimitiveSequence<SingleTrait, Element>

Completed

public typealias Completable = PrimitiveSequence<CompletableTrait, Swift.Never>

Maybe

public typealias Maybe<Element> = PrimitiveSequence<MaybeTrait, Element>

这样一看,他们都是基于PrimitiveSequence的结构体,这个结构体的类型是<Trait, Element>

Trait表示特征;Element姑且称它为元素。但结合上下文我推断Element是一个泛型类型的元素

public struct PrimitiveSequence<Trait, Element> {
    let source: Observable<Element>
​
    init(raw: Observable<Element>) {
        self.source = raw
    }
}

再往后面看,PrimitiveSequence的扩展继承于ObservableConvertibleType协议

在协议ObservableConvertibleType内部,我们看到了asObservable()方法,并在对于PrimitiveSequence的扩展中实现了这个函数方法

public protocol ObservableConvertibleType {
    /// Type of elements in sequence.
    associatedtype Element
​
    @available(*, deprecated, renamed: "Element")
    typealias E = Element
​
    /// Converts `self` to `Observable` sequence.
    ///
    /// - returns: Observable sequence that represents `self`.
    func asObservable() -> Observable<Element>
}
extension PrimitiveSequence: ObservableConvertibleType {
    /// Converts `self` to `Observable` sequence.
    ///
    /// - returns: Observable sequence that represents `self`.
    public func asObservable() -> Observable<Element> {
        return self.source
    }
}

这不就是告诉我们,Single、Completed、Maybe是可以通过asObservable(),转化为Observable序列

还有一点要声明的是Observable为Class类型,继承于ObservableType;同时它的扩展支持对特征序列的转换:

asSingle()
asMaybe()
asCompletable()

b.特征

public enum SingleTrait { }
public enum CompletableTrait { }
public enum MaybeTrait { }

Xnip2021-07-26_16-40-30.jpg

以上每个序列都独立的特征,这里的特征我认为是对于PrimitiveSequenceType协议的扩展。而扩展的内容每个都不同,这是Rx中对于特征序列所封装的内部方法,如图上,就比如特征序列对于操作符的支持,而以上的扩展支持我们统称为----序列特征。

支持UI的序列

Driver:

public typealias Driver<Element> = SharedSequence<DriverSharingStrategy, Element>
​
public struct DriverSharingStrategy: SharingStrategyProtocol {
    public static var scheduler: SchedulerType { return SharingScheduler.make() }
    public static func share<Element>(_ source: Observable<Element>) -> Observable<Element> {
        return source.share(replay: 1, scope: .whileConnected)
    }
}
​
public enum SharingScheduler {
    /// Default scheduler used in SharedSequence based traits.
    public private(set) static var make: () -> SchedulerType = { MainScheduler() }
}

从源文件Driver,可以看出有两个名字Driver和DriverSharingStrategy,分别代表了什么呢?

Driver依然是一个别名,它实际上是SharedSequence(共享序列)的结构体

DriverSharingStrategy继承于SharingStrategyProtocol协议,这个协议只做两件事--控制线程调度、控制共享策略,而DriverSharingStrategy实现的内容,就是Driver其中特征的一部分

一定在主线程监听(被明确在主线程调用)
共享状态变化(控制监听同一个序列的共享状态)

ObservableConvertibleType+Driver:

基于ObservableConvertibleType扩展的协议有三个asDriver方法,它的解释是:

Converts observable sequence to `Driver` trait.

举两个例子

public func asDriver(onErrorRecover: @escaping (_ error: Swift.Error) -> Driver<Element>) -> Driver<Element> {
  let source = self
      .asObservable()
      .observeOn(DriverSharingStrategy.scheduler)
      .catchError { error in
        onErrorRecover(error).asObservable()
      }
    return Driver(source)
}
public func asDriver(onErrorJustReturn: Element) -> Driver<Element> {
  let source = self
      .asObservable()
      .observeOn(DriverSharingStrategy.scheduler)
      .catchErrorJustReturn(onErrorJustReturn)
          return Driver(source)
}

catchError会在要发生onError事件时将其拦截,并使用备用的序列替代它,再次执行操作。

catchErrorJustReturn在获取到错误的时候返回一个备用的值。

以上例子也印证了它的另一个特征

不会产生error事件(error事件已经提前预处理了)
同时,还调用了主线程监听.observeOn(DriverSharingStrategy.scheduler)

虽然我们看到了这三个特征的的确确和Driver有关,但如何把它串联起来,看到这里就会豁然开朗:

Driver(source) = SharedSequence(source)
init(_ source: Observable<Element>) {
  self._source = SharingStrategy.share(source)
}

后话说

在上面的部分,PrimitiveSequence继承于ObservableConvertibleType协议,Driver也扩展了这个协议,根据它的使用场景,可以得出它是一个基于序列转换的根协议

发掘趣味

Driver(ControlEvent、ControlProperty)

public func asDriver() -> Driver<Element> {
        return self.asDriver { _ -> Driver<Element> in
            #if DEBUG
                rxFatalError("Somehow driver received error from a source that shouldn't fail.")
            #else
                return Driver.empty()
            #endif
        }
    }

Driver(BehaviorRelay)

public func asDriver() -> Driver<Element> {
        let source = self.asObservable()
            .observeOn(DriverSharingStrategy.scheduler)
        return SharedSequence(source)
    }

d.即是序列也是观察者

最常用的是PublishSubject、BehaviorRelay。

PublishSubject只会发出订阅后产生的元素,对于订阅前产生的元素不会发出

BehaviorRelay会发出最新的元素,如果没有新元素就发出默认的元素,然后再发出新元素

subjects即是可监听序列也是观察者,在分析源码的时候我们看到subjects是Class类型,继承了:

Observable<Element>
SubjectType
ObserverType

有关SubjectType的解释,充分说明了它的定义

/// Represents an object that is both an observable sequence as well as an observer.

特殊的Subjects是RxRelay。他和 Subjects 相似,唯一的区别是不会接受 onErroronCompleted 这样的终止事件。因为一旦subjects接收到onErroronCompleted他就无法继续工作了,也不会转发后续任何事件

PublishRelay == PublishSubject(remove onError或onCompleted);PublishSubject符合ObserverType协议,PublishRelay不符合
BehaviorRelay == BehaviorSubject(remove onError或onCompleted)

e.操作符

操作符是依托于序列的操作,帮助对原始序列进行变化和重组,也可以创建新序列 。同时,操作符使序列具备处理逻辑的能力,并通过这种能力达到我们想要的结果。

思考

关于此前学习的误区,在掌握了rxswift核心部分后,希望尽快熟悉并理解它的API各个用法帮助我在使用中游刃有余,但这是一种比较耗时的学习方法。而应该转变为"你想用它做什么,它能为你做什么",我们知道它是一个函数响应式编程思想,而它的作用其实就是在一段逻辑关系中起到一个「桥」的作用。这样理解,我们就能知道在代码的哪些地方可以使用rxswift,帮助我们提高生产效率,而我们常说的万物皆rx也要根据具体的实际情况来。

如果你和我一样是一个正在学习的初学者,不建议在公司代码中大范围的使用,很多人都想一口吃一个胖子,但这样的学习过程既疲劳也煎熬,学习成果不是一蹴而就的,多看多想总能找到正确的路线,以上是我在学习rxswift中遇到的瓶颈和反思,希望写出来对其它想学习的人能有所帮助。