avatar

RxSwift从入门到放弃

引子

现在主要开发工作还是在UIKit上,由于RxSwift的简介性,还是学一学这个优秀的响应式的开发模式。

先来看一个例子

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
40
41
42
import UIKit
import RxSwift
import RxCocoa

class LearnRxSwift: UIViewController {

struct Music {
let name: String
let singer: String
}

var MusicList = Observable.just([
Music(name: "无条件", singer: "陈奕迅"),
Music(name: "你曾是少年", singer: "S.H.E"),
Music(name: "从前的我", singer: "陈洁仪"),
Music(name: "在木星", singer: "朴树")
])

let disposeBag = DisposeBag()

var tableView: UITableView!

override func viewDidLoad() {
super.viewDidLoad()

tableView = UITableView(frame: CGRect(x: 0, y: 0, width: screen.width, height: screen.height))
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "musicCell")
view.addSubview(tableView)

MusicList.bind(to: tableView.rx.items(cellIdentifier: "musicCell")) { _, music, cell in
cell.textLabel?.text = music.name
cell.detailTextLabel?.text = music.singer
}.disposed(by: disposeBag)

tableView.rx.modelSelected(Music.self).subscribe(onNext: { music in
print("你选中的歌曲信息【\(music)】")
}).disposed(by: disposeBag)


}

}
image-20200410194335225

代码量相比之前复杂的Delegate方法还是少了很多,话不多说,开始进入学习吧。

本文参考:https://www.hangge.com/

基础知识

Observable

Observale

  • 可以看到Obserable是一个可观察泛型队列,会不定期的发送Event(element: T)
  • 既然有了Obserable,就有一个观察他的Observer

Event

1
2
3
4
5
6
7
8
9
10
public enum Event<Element> {
/// Next element is produced.
case next(Element)

/// Sequence terminated with an error.
case error(Swift.Error)

/// Sequence completed successfully.
case completed
}

从枚举可以看出,Event有三种类型,next就是事件,error表示错误,completed表示完成。当error和completed发出时,这个Obserable队列都会停止

Observable的创建

just()

1
let ob = Observable<Int>.just(5)

使用默认值创建,当然的显式声明是可以不用的

of()

1
let ob = Observable.of(123, 1234, 12345)

可以接受可变数量的参数,并且推导参数类型

just()

1
2
let ob = Observable.from([123, 1234, 12345])
// let ob = Observable<[Int]>.just([123, 1234, 12345])

可以接受数组参数,和下面的just()方法是同样的效果

empty()、never()

1
2
let ob = Observable<Int>.empty()
// let ob = Observable<Int>.never()

创建一个空序列,创建一个不发出也不终止的序列

error()

1
let ob = Observable<Int>.error(Error)

创建一个没有操作,直接发送一个错误的序列

range()

1
let ob = Observable.range(start: 1, count: 5)

类似于python的range函数?但是步长都是1

repeatElement()

1
let ob = Observable.repeatElement(1)

创建一个只会发送1的复读机

generate()

1
2
3
4
5
let ob = Observable.generate(
initialState: 0,
condition: { $0 <= 10 },
iterate: { $0 + 2 }
)

没想到吧,我是个for循环

create()

1
2
3
4
5
let ob = Observable<Int>.create { observer in
observer.onNext(123)
observer.onCompleted()
retrun Disposables.create()
}

用闭包写了个函数

defered()

1
2
3
4
5
6
7
8
9
var para = "Char"
let factory = Observable<Any>.deferred {
switch para {
case "Int":
return Observable.of(1, 2, 3, 4)
case "Char":
return Observable.of("a", "b", "c", "d")
}
}

根据参数值来有不同的初始化

interval()

1
2
3
4
let ob = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
ob.subscribe { event in
print(event)
}

间隔时间生成从0开始的索引数

image-20200418202912251

timer()

1
2
let ob = Observable<Int>.timer(5, scheduler: MainScheduler.instance) // 延时5秒后发出一个
// let ob = Observable<Int>.timer(5, period: 1, scheduler: MainScheduler.instance) // 延时5秒后间隔1秒重复发出

高级interval,加入了延迟和可选重复功能

Obserable的订阅

既然创建了序列,那么也需要接收方法

不分类

1
2
3
4
let ob = Observable.of("a", "b", "c")
ob.subscribe { event in
print(event)
}
image-20200418214747770
1
2
3
4
let ob = Observable.of("a", "b", "c")
ob.subscribe { event in
print(event.element)
}
image-20200418214826117

分类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let ob = Observable.of("a", "b", "c")
ob.subscribe (
onNext: { element in
print(element)
},
onError: { error in
print(error)
},
onCompleted: {
print("Completed")
},
onDisposed: {
print("Disposed")
}
)
image-20200418215356895

当然四种类型都是可选的,可以只有onNext

监听事件的生命周期

doOn、doAfterOn

1
2
3
4
5
6
7
8
9
10
11
12
let ob = Observable.of("a", "b", "c")
ob
.do(onNext: { element in
print("will print \(element)")
}, afterNext: { element in
print("did print \(element)")
})
.subscribe (
onNext: { element in
print(element)
}
)
image-20200418220254598

dispose

1
2
3
4
let ob = Observable.of("a", "b", "c")
ob.subscribe { event in
print(event.element)
}.dispose()

一个序列只有在被订阅时(subscribe)才发送event,使用dispose()来防止内存泄漏,在一个序列不需要再使用时,调用dispose()方法

或者使用disposeBag

1
2
3
4
5
6
7
8
9
let disposeBag = DisposeBag()
let ob1 = Observable.of("a", "b", "c")
ob1.subscribe { event in
print(event.element)
}.disposed(by: disposeBag)
let ob1 = Observable.of("e", "f", "g")
ob2.subscribe { event in
print(event.element)
}.disposed(by: disposeBag)

disposeBag相当于一个垃圾桶,会把订阅行为储存起来,在自身即将dealloc的时候(意思是只要disposeBag不销毁,订阅行为还存在),对其里面的行为调用dispose()

Observer

我们知道doOn可以监听事件的生命周期,当然了,Obserable一定对应着Observer,这不,来了,他就是用来监听事件的

在subscribe、bind方法中创建观察者

subscribe上文已经说过了,来说说bind方法

bind

1
2
3
4
5
6
7
8
let ob = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
ob
.map {
"当前索引: \($0)"
}
.bind { text in
self.label.text = text
}

Apr-18-2020 23-59-58

使用AnyObserver

subscribe

1
2
3
4
5
6
7
8
9
10
11
12
let observer = AnyObserver<Int> { event in
switch event {
case .next(let data):
print(data)
case .error(let error):
print(error)
case .completed:
print("completed")
}
}

ob.subscribe(observer)

bind

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let observer = AnyObserver<String> { event in
switch event {
case .next(let text):
self.label.text = text
default:
break
}
}

ob
.map {
"当前索引: \($0)"
}
.bind(to: observer)

Binder

Binder比起AnyObserver主要有两个特征:

  • 不会处理错误
  • 确保绑定都是在给定Scheduler上执行
1
2
3
4
5
6
7
8
9
let observer = Binder<String>(label) { view, text in
view.text = text
}

ob
.map {
"当前索引: \($0)"
}
.bind(to: observer)

自定义可绑定属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.... // viewDidLoad
let ob = Observable<Int>.interval(0.5, scheduler: MainScheduler.instance)
ob
.map { CGFloat($0) }
.bind(to: label.rx.fontSize)
....

extension Reactive where Base: UILabel {
public var fontSize: Binder<CGFloat> {
return Binder(self.base) { label, fontSize in
label.font = UIFont.systemFont(ofSize: fontSize)
}
}
}

RxSwift自带的可绑定属性

就像我们自定义的fontSize属性一样,其实已经自带了一些,比如UILabel里的text,所以之前的操作可以变为

1
2
3
ob
.map { "当前索引: \($0)" }
.bind(to: label.rx.text)

Subject

  1. 在使用ObserverObserable的时候,我们总是需要预先设定好队列中的数据,有没有可以动态调整序列的对象呢,于是就有了Subject

  2. Subject一共有四种,分别是PublishSubjectBehaviorSubjectReplaySubjectVariable(已被替换为BehaviorRelay区别在于:

  • 首先他们都是 Observable,他们的订阅者都能收到他们发出的新的 Event
  • 直到 Subject 发出 .complete 或者 .errorEvent 后,该 Subject 便终结了,同时它也就不会再发出 .next 事件。
  • 对于那些在 Subject 终结后再订阅他的订阅者,也能收到 Subject 发出的一条 .complete.errorEvent,告诉这个新的订阅者它已经终结了。
  • 他们之间最大的区别只是在于:当一个新的订阅者刚订阅它的时候,能不能收到 Subject 以前发出过的旧 Event,如果能的话又能收到多少个。
  1. Subject新添加数据的方法主要有3种
    • onNext(:)/on(.next(:))
    • onError(:)/on(.error(:))
    • onCompleted(:)/on(.completed(:))

具体区别

  • PublishSubjectReplaySubject不需要初始值,而另两种需要
  • BehaviorSubjectBehaviorRelay的bufferSize都为1(概念看图容易理解)

IMG_D157EB501F50-1

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//      let subject = PublishSubject<String>()
// let subject = BehaviorSubject<String>(value: "aaa")
let subject = ReplaySubject<String>.create(bufferSize: 2)
subject.onNext("aaa")
subject.onNext("bbb")
subject.onNext("ccc")
subject.onNext("ddd")

// 订阅A, 这时应该是ccc和ddd
subject.subscribe { event in
print("订阅A -- ", event)
}.disposed(by: disposeBag)

subject.onNext("eee")
subject.onCompleted()
// 订阅B, 此时应为ddd, eee, completed
subject.subscribe { event in
print("订阅B -- ", event)
}.disposed(by: disposeBag)
image-20200419114447646

BahaviorRelay的用法稍微有些不同,一般用于下拉加载的List中,它不需要completed或者error来终止,看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let subject = BehaviorRelay<[Int]>(value: [1, 2])

subject.subscribe { event in
print("订阅A -- ", event)
}.disposed(by: disposeBag)

subject.accept(subject.value + [3, 4])

subject.subscribe { event in
print("订阅B -- ", event)
}.disposed(by: disposeBag)

subject.accept(subject.value + [5, 6])

subject.subscribe { event in
print("订阅C -- ", event)
}.disposed(by: disposeBag)
image-20200419115150427
文章作者: X Mεl0n
文章链接: http://www.zrzz.site/2020/04/10/RxSwift%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E6%94%BE%E5%BC%83/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 X Mεl0n | 随手记

评论