RxSwift 文档:Observable 和 Observer

Observer - 观察者


AnyObserver


AnyObserver 可以用来描叙任意一种观察者。

例如:

打印网络请求结果:

1
2
3
4
5
6
7
URLSession.shared.rx.data(request: URLRequest(url: url))
.subscribe(onNext: { data in
print("Data Task Success with count: \(data.count)")
}, onError: { error in
print("Data Task Error: \(error)")
})
.disposed(by: disposeBag)

可以看作是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let observer: AnyObserver<Data> = AnyObserver { (event) in
switch event {
case .next(let data):
print("Data Task Success with count: \(data.count)")
case .error(let error):
print("Data Task Error: \(error)")
default:
break
}
}

URLSession.shared.rx.data(request: URLRequest(url: url))
.subscribe(observer)
.disposed(by: disposeBag)

用户名提示语是否隐藏:

1
2
3
usernameValid
.bind(to: usernameValidOutlet.rx.isHidden)
.disposed(by: disposeBag)

可以看做是:

1
2
3
4
5
6
7
8
9
10
11
12
let observer: AnyObserver<Bool> = AnyObserver { [weak self] (event) in
switch event {
case .next(let isHidden):
self?.usernameValidOutlet.isHidden = isHidden
default:
break
}
}

usernameValid
.bind(to: observer)
.disposed(by: disposeBag)

Binder


Binder 主要有以下两个特征:

  • 不会处理错误事件
  • 确保绑定都是在给定 Scheduler 上执行(默认 MainScheduler

一旦产生错误事件,在调试环境下将执行 fatalError,在发布环境下将打印错误信息。

AnyObserver 时,我们举了一个这样的例子:

1
2
3
4
5
6
7
8
9
10
11
12
let observer: AnyObserver<Bool> = AnyObserver { [weak self] (event) in
switch event {
case .next(let isHidden):
self?.usernameValidOutlet.isHidden = isHidden
default:
break
}
}

usernameValid
.bind(to: observer)
.disposed(by: disposeBag)

由于这个观察者是一个 UI 观察者,所以它在响应事件时,只会处理 next 事件,并且更新 UI 的操作需要在主线程上执行。

因此一个更好的方案就是使用 Binder

1
2
3
4
5
6
7
let observer: Binder<Bool> = Binder(usernameValidOutlet) { (view, isHidden) in
view.isHidden = isHidden
}

usernameValid
.bind(to: observer)
.disposed(by: disposeBag)

复用

由于页面是否隐藏是一个常用的观察者,所以应该让所有的 UIView 都提供这种观察者:

1
2
3
4
5
6
7
8
9
10
11
extension Reactive where Base: UIView {
public var isHidden: Binder<Bool> {
return Binder(self.base) { view, hidden in
view.isHidden = hidden
}
}
}

usernameValid
.bind(to: usernameValidOutlet.rx.isHidden)
.disposed(by: disposeBag)

这样你不必为每个 UI 控件单独创建该观察者。这就是 usernameValidOutlet.rx.isHidden 的由来,许多 UI 观察者 都是这样创建的:

  • 按钮是否可点击 button.rx.isEnabled
1
2
3
4
5
6
7
extension Reactive where Base: UIControl {
public var isEnabled: Binder<Bool> {
return Binder(self.base) { control, value in
control.isEnabled = value
}
}
}
  • label 的当前文本 label.rx.text
1
2
3
4
5
6
7
extension Reactive where Base: UILabel {
public var text: Binder<String?> {
return Binder(self.base) { label, text in
label.text = text
}
}
}

Observable & Observer 既是可被监听的序列也是观察者


在我们所遇到的事物中,有一部分非常特别。它们既是可被监听的序列也是观察者。

AsyncSubject


AsyncSubject 将在源 Observable 产生完成事件后,发出最后一个元素(仅仅只有最后一个元素),如果源 Observable 没有发出任何元素,只有一个完成事件。那 AsyncSubject 也只有一个完成事件。
它会对随后的观察者发出最终元素。如果源 Observable 因为产生了一个 error 事件而中止, AsyncSubject 就不会发出任何元素,而是将这个 error 事件发送出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let disposeBag = DisposeBag()
let subject = AsyncSubject<String>()

subject
.subscribe { print("Subscription: 1 Event:", $0) }
.disposed(by: disposeBag)

subject.onNext("🐶")
subject.onNext("🐱")
subject.onNext("🐹")
subject.onCompleted()

// Subscription: 1 Event: next(🐹)
// Subscription: 1 Event: completed

PublishSubject


PublishSubject 将对观察者发送订阅后产生的元素,而在订阅前发出的元素将不会发送给观察者。如果你希望观察者接收到所有的元素,你可以通过使用 Observablecreate 方法来创建 Observable,或者使用 ReplaySubject

如果源 Observable 因为产生了一个 error 事件而中止, PublishSubject 就不会发出任何元素,而是将这个 error 事件发送出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let disposeBag = DisposeBag()
let subject = PublishSubject<String>()

subject
.subscribe { print("Subscription: 1 Event:", $0) }
.disposed(by: disposeBag)

subject.onNext("🐶")
subject.onNext("🐱")

subject
.subscribe { print("Subscription: 2 Event:", $0) }
.disposed(by: disposeBag)

subject.onNext("🅰️")
subject.onNext("🅱️")

// Subscription: 1 Event: next(🐶)
// Subscription: 1 Event: next(🐱)
// Subscription: 1 Event: next(🅰️)
// Subscription: 2 Event: next(🅰️)
// Subscription: 1 Event: next(🅱️)
// Subscription: 2 Event: next(🅱️)

ReplaySubject


ReplaySubject 将对观察者发送全部的元素,无论观察者是何时进行订阅的。

这里存在多个版本的 ReplaySubject,有的只会将最新的 n 个元素发送给观察者,有的只会将限制时间段内最新的元素发送给观察者。

如果把 ReplaySubject 当作观察者来使用,注意不要在多个线程调用 onNext, onErroronCompleted。这样会导致无序调用,将造成意想不到的结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let disposeBag = DisposeBag()
let subject = ReplaySubject<String>.create(bufferSize: 1)

subject
.subscribe { print("Subscription: 1 Event:", $0) }
.disposed(by: disposeBag)

subject.onNext("🐶")
subject.onNext("🐱")

subject
.subscribe { print("Subscription: 2 Event:", $0) }
.disposed(by: disposeBag)

subject.onNext("🅰️")
subject.onNext("🅱️")

// Subscription: 1 Event: next(🐶)
// Subscription: 1 Event: next(🐱)
// Subscription: 2 Event: next(🐱)
// Subscription: 1 Event: next(🅰️)
// Subscription: 2 Event: next(🅰️)
// Subscription: 1 Event: next(🅱️)
// Subscription: 2 Event: next(🅱️)

BehaviorSubject


当观察者对 BehaviorSubject 进行订阅时,它会将源 Observable 中最新的元素发送出来(如果不存在最新的元素,就发出默认元素)。然后将随后产生的元素发送出来。
如果源 Observable 因为产生了一个 error 事件而中止, BehaviorSubject 就不会发出任何元素,而是将这个 error 事件发送出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
let disposeBag = DisposeBag()
let subject = BehaviorSubject(value: "🔴")

subject
.subscribe { print("Subscription: 1 Event:", $0) }
.disposed(by: disposeBag)

subject.onNext("🐶")
subject.onNext("🐱")

subject
.subscribe { print("Subscription: 2 Event:", $0) }
.disposed(by: disposeBag)

subject.onNext("🅰️")
subject.onNext("🅱️")

subject
.subscribe { print("Subscription: 3 Event:", $0) }
.disposed(by: disposeBag)

subject.onNext("🍐")
subject.onNext("🍊")

// Subscription: 1 Event: next(🔴)
// Subscription: 1 Event: next(🐶)
// Subscription: 1 Event: next(🐱)
// Subscription: 2 Event: next(🐱)
// Subscription: 1 Event: next(🅰️)
// Subscription: 2 Event: next(🅰️)
// Subscription: 1 Event: next(🅱️)
// Subscription: 2 Event: next(🅱️)
// Subscription: 3 Event: next(🅱️)
// Subscription: 1 Event: next(🍐)
// Subscription: 2 Event: next(🍐)
// Subscription: 3 Event: next(🍐)
// Subscription: 1 Event: next(🍊)
// Subscription: 2 Event: next(🍊)
// Subscription: 3 Event: next(🍊)

ControlProperty


ControlProperty 专门用于描述 UI 控件属性的,它具有以下特征:

  • 不会产生 error 事件
  • 一定在 MainScheduler 订阅(主线程订阅)
  • 一定在 MainScheduler 监听(主线程监听)
  • 共享状态变化