RxSwift 入门以及了解

前言介绍

RX是一个帮助我们简化异步编程的框架。它拓展了观察者模式,使我们可以自由组合多个异步事件,而不需要去关心线程,同步,线程安全,并发数据以及I/O阻塞。RXSwift是RX的Swift版本。

为什么要使用RxSwift

  • 复合
  • 复用
  • 清晰
  • 易用 - 因为它抽象了异步编程,使我们统一了代码风格
  • 稳定 - 因为Rx使完全通过单元测试的
Target Action
button.rx.tap.subscribe(onNext: {
    print("button Tapped")
})
.disposed(by: disposeBag)
代理
scrollView.rx.contentOffset
            .subscribe(onNext: { contentOffset in
                print("contentOffset: \(contentOffset)")
            })
            .disposed(by: disposeBag)
闭包回调
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)
通知
NotificationCenter.default.rx
        .notification(.UIApplicationWillEnterForeground)
        .subscribe(onNext: { (notification) in
            print("Application Will Enter Foreground")
        })
        .disposed(by: disposeBag)

不需要去管理观察者的生命周期,这样你就有更多精力去关注业务逻辑。

KVO
user.rx.observe(String.self, #keyPath(User.name))
        .subscribe(onNext: { newValue in
            print("do something with newValue")
        })
        .disposed(by: disposeBag)
多个任务之间有依赖关系
/// 用 Rx 封装接口
enum Api {

    /// 通过用户名密码取得一个 token
    static func token(username: String, password: String) -> Observable { ... }

    /// 通过 token 取得用户信息
    static func userInfo(token: String) -> Observable { ... }
}
/// 通过用户名和密码获取用户信息
Api.token(username: "beeth0ven", password: "987654321")
    .flatMapLatest(Api.userInfo)
    .subscribe(onNext: { userInfo in
        print("获取用户信息成功: \(userInfo)")
    }, onError: { error in
        print("获取用户信息失败: \(error)")
    })
    .disposed(by: disposeBag)
等待多个并发任务完成后处理结果
/// 用 Rx 封装接口
enum Api {

    /// 取得老师的详细信息
    static func teacher(teacherId: Int) -> Observable { ... }

    /// 取得老师的评论
    static func teacherComments(teacherId: Int) -> Observable<[Comment]> { ... }
}
/// 同时取得老师信息和老师评论
Observable.zip(
      Api.teacher(teacherId: teacherId),
      Api.teacherComments(teacherId: teacherId)
    ).subscribe(onNext: { (teacher, comments) in
        print("获取老师信息成功: \(teacher)")
        print("获取老师评论成功: \(comments.count) 条")
    }, onError: { error in
        print("获取老师信息或评论失败: \(error)")
    })
    .disposed(by: disposeBag)

函数响应式编程

[图片上传失败...(image-6266d-1598506756093)]

Observable 理解成Signal,订阅Signal去完成一些处理操作。比如绑定数据,最后销毁这个Signal。

函数式编程是种编程范式,它需要我们将函数作为参数传递,或者作为返回值返还。我们可以通过组合不同的函数来得到想要的结果。

对每个事件都做出一些响应,所以这种编程叫做函数响应式编程。我们通过不同的构建函数,来创建所需要的数据序列。最后通过适当的方式来响应这个序列。这就是函数响应式编程。

数据绑定

数据绑定(订阅):就是指将可被监听的序列绑定到观察者上。

比如:

let image: Observable = ...
image.bind(to: imageView.rx.image)

将一个图片序列 “同步” 到imageView上。这个序列里面的图片可以是异步产生的。这里定义的 image 就是上图中蓝色部分(可被监听的序列)。这种“同步机制”就是数据绑定(订阅)

RXSwift核心

[图片上传失败...(image-9ce6ac-1598506756094)]

  • Observable - 产生事件(即信号)
  • Observer - 响应事件(即绑定信号后的处理)
  • Operator - 创建变化组合事件(组合多个信号,并行或者串行)
  • Disposable - 管理绑定(订阅)的生命周期(一般管理信号销毁居多)
  • Schedules - 线程队列调配
// Observable
let text = usernameOutlet.rx.text.orEmpty.asObservable()

// Observable
let passwordValid = text
    // Operator
    .map { $0.characters.count >= minimalUsernameLength }

// Observer
let observer = passwordValidOutlet.rx.isHidden

// Disposable
let disposable = passwordValid
    // Scheduler 用于控制任务在那个线程队列运行
    .subscribeOn(MainScheduler.instance)
    .observeOn(MainScheduler.instance)
    .bind(to: observer)


...

// 取消绑定,你可以在退出页面时取消绑定
disposable.dispose()

Observable - 可被监听的序列

所有的事物都是序列,是信号。它能异步的触发一系列事件并携带不可更改的状态变量。简单来说,它能让某个类的实例在一段事件内实现对另一个实例对象值的观察。例如:观察者可以捕获对所有可观察对象触发的事件,从而实现UI的实时更新或者是数据的实时处理。

subscribe后面的onNext,onError, onCompleted 分别响应。这些为事件。completed事件和error事件都会终止可观察对象的生命周期。不会继续触发新的事件。

public enum Event {
    case next(Element)
    case error(Swift.Error)
    case completed
}
  • next - 序列产生了一个新的元素
  • error - 创建序列时产生了一个错误,导致序列终止
  • completed - 序列的所有元素都已经成功产生,整个序列已经完成
Single
  • 要么产生一个元素,要么产生一个error事件
  • 不会共享状态变化
Completable
  • 发出零个元素
  • 发出一个completed事件或者一个error事件
  • 不会共享状态变化
Maybe
  • 发出一个元素或者一个completed事件或者一个error事件
  • 不会共享状态变化
Driver
  • 不会产生error事件
  • 一定在MainScheduler监听(主线程监听)
  • 共享状态变化
ControlEvent

专门用于描述UI控件所产生的事件。

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

Sequences - 序列

有限观察序列 (Finite observable sequences)

该序列是指那些最后会以completed或者error事件终止生命周期的可观察对象。
比如网络任务:

API.download(file: "http://www...")
    .subscribe( onNext: { data in
                        append data to temporary file }, 
                onError: { error in
                        display error to user }, 
                onCompleted: {
                        use downloaded file })
无限观察序列 (Infinite observable sequences)

与网络任务不同的是,UI以及交互事件都是无限观察序列。它们并不存在一个明确的生命周期终止。
比如:

UIDevice.rx.orientation.subscribe(onNext: { current in 
    switch current { 
        case .landscape:
            re-arrange UI for landscape 
        case .portrait:
            re-arrange UI for portrait 
    } 
})

Observer - 观察者

观察者是用来监听事件,然后它需要这个事件做出响应。

AnyObserver

可以用来描述任意一种观察者

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

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

Operator - 操作符

操作符可以帮助我们创建新的序列,或者变化组合原有的序列,从而生成一个新的序列。

filter - 过滤
map - 转换
zip - 配对

还有很多,这里暂时不展开太多。

Disposable - 可被清除的资源

通常来说,一个序列如果发出了error或者completed事件,那么所有内部资源都会被释放。如果需要提前释放或取消订阅,那么可以使用dispose方法。

一般我们推荐使用清除包(DisposeBag)或者takeUntil操作符来管理订阅的生命周期。

DisposeBag - 清除包
var disposeBag = DisposeBag()

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    textField.rx.text.orEmpty
        .subscribe(onNext: { text in print(text) })
        .disposed(by: self.disposeBag)
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    self.disposeBag = DisposeBag()
}

清除包被释放的时候,清除包内部所有可被清除的资源(Disposable)都将被清除。

takeUntil
override func viewDidLoad() {
    super.viewDidLoad()

    ...

    _ = usernameValid
        .takeUntil(self.rx.deallocated)
        .bind(to: passwordOutlet.rx.isEnabled)

    _ = usernameValid
        .takeUntil(self.rx.deallocated)
        .bind(to: usernameValidOutlet.rx.isHidden)

    _ = passwordValid
        .takeUntil(self.rx.deallocated)
        .bind(to: passwordValidOutlet.rx.isHidden)

    _ = everythingValid
        .takeUntil(self.rx.deallocated)
        .bind(to: doSomethingOutlet.rx.isEnabled)

    _ = doSomethingOutlet.rx.tap
        .takeUntil(self.rx.deallocated)
        .subscribe(onNext: { [weak self] in self?.showAlert() })
}

这将使得订阅一直持续到控制器的dealloc事件产生为止。

Schedulers - 调度器

SchedulesRX实现多线程的核心模块,主要用于控制任务在哪个线程或队列运行。

在线程这部分主要有两个操作符:observeOn 和 subscribeOn ,常用的还是 observeOn 。

调用 observeOn 指定接下来的操作在哪个线程。
调用 subscribeOn 决定订阅者的操作执行在哪个线程。

let rxData: Observable = ...

rxData
    .subscribeOn(ConcurrentDispatchQueueScheduler(qos: .userInitiated))
    .observeOn(MainScheduler.instance)
    .subscribe(onNext: { [weak self] data in
        self?.data = data
    })
    .disposed(by: disposeBag)
使用subscribeOn

subscribeOn来决定数据序列的构建函数在哪个Scheduler

使用observeOn

observeOn来决定在哪个Scheduler监听这个数据序列.

一个比较典型的例子就是,在后台发起网络请求,然后解析数据,最后在主线程刷新页面。你就可以先用 subscribeOn 切到后台去发送请求并解析数据,最后用 observeOn 切换到主线程更新页面。

MainScheduler

主线程

SerialDispatchQueueScheduler

串行队列

ConcurrentDispatchQueueScheduler

并行队列

OperationQueueScheduler

可以理解成NSOperationQueue.

Error Handling - 错误处理

一旦序列里面发生了一个error事件,整个序列将被终止。

  • retry - 重试
  • catch - 恢复
retry
retryWhen

延时后重试

catchError

catchError可以在错误产生时,用一个备用元素或者一组备用元素将错误替换掉。

result

可以将错误转换成result,即使发生了错误,序列也不会终止。

Subject

Subject是一个桥梁,既是 Observable 也是 Observer

  • 作为一个 Observer,它可以订阅序列
  • 同时作为一个Obserable,它可以转发或者发射数据。

Subject有几种:

  • PublishSubject
  • ReplaySubject
  • BehaviorSubject
  • Variable
PublishSubject

当有观察者订阅PublishSubject时,PublishSubject会发射订阅之后的数据给这个观察者。(存在数据丢失的问题)

ReplaySubject

和PublishSubject不同,不论观察者什么时候订阅,ReplaySubject都会发射完整的数据给观察者。

BehaviorSubject

当一个观察者订阅一个BehaviorSubject,它会发送原序列最近的那个值(如果还没有值会有默认的),之后继续发射原序列的值。

Variable

Variable是BehaviorSubject的一个封装,但是不会因为错误终止也不会正常终止,是一个无限序列。

注意的问题

cell中使用button重复订阅的问题

解决办法:为Cell添加一个DisposeBag,在prepareForReuse时,更换新的DisposeBag

var disposeBag = DisposeBag()

override func prepareForReuse() {
    super.prepareForReuse()
    disposeBag = DisposeBag()
}

冷、热信号的问题

热信号,一般是我们接触到的最新的东西。
冷信号,一般是依赖其他的信号订阅后的行为,比如一些异步操作。

耦合性问题

RXSwift滥用会导致项目耦合性变差,可读性和可维护性都会变得很差,并且有一定的学习成本,会给项目本身带来一定的风险。

循环引用问题

RXSwift使用过程中会大量使用block,使用不当容易导致循环引用。

你可能感兴趣的:(RxSwift 入门以及了解)