RxSwift学习--核心逻辑初探

805 阅读7分钟

前言

写这篇文章是为了记录下自己在对于RxSwift的学习过程中的概念理解,操作步骤以及心得体会,以便于在以后复习所用。如果文中有任何错误的地方,还请各位看官老爷们指正...(先捂上脸🤦)

函数响应式编程

在学习RxSwift之前,先来了解一下函数响应式编程的思想,我们可以把函数响应式编程拆开来看,分为函数式编程和响应式编程来理解:

1.函数式编程

函数式编程简称FP(Functional Programming),函数式编程就是一种抽象程度很高的编程范式,它将计算机运算看做是数学中函数的计算,而纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。

函数式编程的一个特点就是:允许把函数本身作为参数传入另一个函数,同时还允许返回一个函数!

函数表达式: y = f(x) ---> x = f(x) ---> y = f(f(x))

下面写一个栗子🌰来理解一下: 有这样一个需求:对于数组[1,2,3,4,5,6,7],首先获取 > 3的数字,获取到的数字之后 + 1,再输出所有数字中的偶数

let array = [1,2,3,4,5,6,7]
for num in array{
            if num > 3{
                let number = num + 1
                if (number % 2 == 0) {
                    print(number)
                  }
            }
        }

这里我们利用行为式思路解决了这个需求,下面来换个思路来解决,使用 Arrayfilter方法;

let array = [1,2,3,4,5,6,7]
 array.filter{ $0 > 3}
            .filter{ ($0+1) % 2 == 0 }
            .forEach { print($0) }

这里 array.filter 函数接受一个闭包类型的参数,filter方法会对 array 中的每一个元素都用传入filter 的闭包调用一遍,根据这个闭包的返回值决定是否将这个元素作为符合条件的元素加入我们的查找结果中。

简单来说我们只需要在传入的闭包中声明好查找的规则,这样我们就完成整个查找操作的处理了。我们这里并没有告诉程序应该怎么去查找满足条件的元素的方法,而只是声明了一个规则。这样做最大的好处就是能够减少我们的代码量,让我们的代码看起来非常的简洁,而且易理解。 这种方式就是函数式编程的一个例子。

2.响应式编程

响应式编程简称RP(Reactive Programming),响应式编程是一种面向数据流和变化传播的异步编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。

简单的来说就是基于事件流的编程方式。事件流就是将要发生事件按照时间顺序排序发生形成的。而当这个事件流产生了返回的数据,可以立刻得到通知并调用回调函数去处理数据。(在现实生活中就是:我在家拿起手机预订了一份外卖,然后商家会收到订单,开始制作外卖,制作完成后会通知骑手取餐,骑手接单取餐,然后送餐,到达目的地,外卖送达,订单完成,这样一系列的事件,先称它为事件流,如果在这些事件流进行的过程中,比如说商家没有出餐,或者骑手没有取餐等等,这些都会导致这个事件流无法完成,而我们可以根据这个事件流中的任何一个事件的结果来进行响应)

从这个小例子可以看到组成响应式编程的三种动作(数据):事件(数据),错误,结束。通过得到这三个响应式动作,我们就可以在程序中作出不同的响应。

这里个人觉得有点很重要,就是要对事件进行“预订/监听”,然后才能收到这个事件的反馈。而对于我而言,我就是监听事件的人,也就是“观察者/订阅方”,监听+观察者是不是就是我们比较熟悉的观察者模式,那么响应式编程就是观察者模式+事件流的控制。

3.函数响应式编程

函数响应式编程 FRP(Functional Reactive Programming)是函数式编程与响应式编程相结合起来的,响应式编程思想为体, 函数式编程思想为用。(比较经典的框架就是RAC和RxSwift),常常有人说,FRP能让你的代码像数学一样简洁,业务像流水一样清晰流畅。

函数响应式

RxSwift初识

1.RxSwift简介

首先,ReactiveX(简写: Rx) 是一个可以帮助我们简化异步编程的框架,简单来说就是基于异步 Event序列的响应式编程,并提供更优雅的数据绑定,可以时刻响应新的数据同时顺序地处理它们。RxSwift (ReactiveX for Swift),就是ReactiveXSwift版本 ReactiveX家族非常强大,就如同‘毒液家族’一样的强大,除了我后面会学习的 RxSwift 之外,还有 RAC(ReactiveCocoa), RxJava, RxJS, RxKotlin, Rx.NET...等等.

2.RxSwift导入配置

(1) 手动导入

  • RxSwift GitHub 上下载最新的代码,
  • 将下载下来的源码包中 Rx.xcodeproj 拖拽至工程中,
  • Project -> Targets -> General -> Embedded Binaries 配置项, RxSwift.frameworkRxCocoa.framework 添加进来即可
  • 在需要使用 RxSwift的地方import 进来

(2) CocoaPods导入

# Podfile
use_frameworks!
target 'YOUR_TARGET_NAME' do
    pod 'RxSwift', '~> 5.0'
    pod 'RxCocoa', '~> 5.0'
end

替换 YOUR_TARGET_NAME 然后在 Podfile 目录下, 终端输入:

$ pod install

🗣🗣🗣这里说明一下为什么会导入RxSwiftRxCocoa两个库,它们的作用分别是:

  • RxSwift:它只是基于 Swift 语言的 Rx 标准实现接口库,所以 RxSwift 里不包含任何Cocoa或者 UI方面的类。
  • RxCocoa:是基于 RxSwift 针对于 iOS 开发的一个库,它通过 Extension 的方法给原生的比如 UI 控件添加了 Rx的特性,使得我们更容易订阅和响应这些控件的事件。

3.RxSwift特性

  • 复合 -Rx 就是和复合的代名词
  • 复用 - 复用性比较强 - 代码量降低
  • 清晰 - 因为声明都是不可变更,代码函数式编程可读性强
  • 易用 - 理解容易,还抽象的了异步编程,统一代码风格
  • 稳定 - 因为 Rx 是完全通过单元测试的
  • 装X - 代码的风格很明显比原⽣好

究竟是不是这样的呢,下面通过一些常用的方式来验证一下:

(1)KVO

一般写法:
func setupKVO() {
    self.person.addObserver(self, forKeyPath: "name", options: .new, context: nil)
}
    
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    person.name = "\(person.name) +"
    // print(person.name)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    print("响应")
    print(change as Any)
}

deinit {
    self.removeObserver(self.person, forKeyPath: "name", context: nil)
}
RxSwift写法:
func setupKVO() {
    self.person.rx.observeWeakly(String.self, "name")
        .subscribe(onNext: { (value) in
            print(value as Any)
        })
        .disposed(by: disposeBag)
}
    
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    person.name = "\(person.name) +"
    // print(person.name)
}

是不是感觉RxSwift版本的KVO写的代码更少了,而且也不用去实现观察者的代理方法,不用关心是否遗漏removeObserver方法。

(2)Target Action

一般写法:
func setupButton() {
   button.addTarget(self, action: #selector(didClickButton), for: .touchUpInside)
}

@objc func didClickButton(){
    print("点我干什么")
}
RxSwift写法:
func setupButton() {
    self.button.rx.tap
      .subscribe(onNext: { () in
            print("点击事件")
        })
      .disposed(by: disposeBag)
}

不需要实现Target Action,代码更加简单了

(3)代理

一般写法:
class ViewController: UIViewController {
    ...
    override func viewDidLoad() {
        super.viewDidLoad()
        scrollView.delegate = self
    }
}

extension ViewController: UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        print("contentOffset: \(scrollView.contentOffset)")
    }
}
RxSwift写法:
class ViewController: UIViewController {
    ...
    override func viewDidLoad() {
        super.viewDidLoad()

        scrollView.rx.contentOffset
            .subscribe(onNext: { contentOffset in
                print("contentOffset: \(contentOffset)")
            })
            .disposed(by: disposeBag)
    }
}

不需要实现代理方法啦,可以直接获取到scrollview的偏移

(4)通知

一般写法:
var testObserver: NSObjectProtocol!

override func viewDidLoad() {
    super.viewDidLoad()

    testObserver = NotificationCenter.default.addObserver(
          forName: .UIApplicationWillEnterForeground,
          object: nil, queue: nil) { (notification) in
        print("Application Will Enter Foreground")
    }
}

deinit {
    NotificationCenter.default.removeObserver(testObserver)
}
RxSwift写法:
override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.rx
        .notification(.UIApplicationWillEnterForeground)
        .subscribe(onNext: { (notification) in
            print("Application Will Enter Foreground")
        })
        .disposed(by: disposeBag)
}

不需要去关注是否已经移除了通知,不会因为没有移除通知而出现崩溃

(5)单击手势

一般写法:
func setupGestureRecognizer(){
    let tap = UITapGestureRecognizer()
    tap.addTarget(self, action: #selector(singleTap(_:)))
    self.label.addGestureRecognizer(tap)
    self.label.isUserInteractionEnabled = true
    
}
    
@objc func singleTap(_ tapGesture: UITapGestureRecognizer) {
    print("点我干嘛")
}
RxSwift写法:
func setupGestureRecognizer(){
        
    let tap = UITapGestureRecognizer()
    self.label.addGestureRecognizer(tap)
    self.label.isUserInteractionEnabled = true
    tap.rx.event.subscribe(onNext: { (tap) in
        print(tap.view)
    })
    .disposed(by: disposeBag)
  }

少写了手势触发实现的方法,代码更简介了

(6)Timer定时器

一般写法:
var testtimer = Timer()

func setupTimer() {
    testtimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(UpdateTimer), userInfo: nil, repeats: true)
    testtimer.fire();
    RunLoop.current.add(testtimer, forMode: .common)
}

@objc func UpdateTimer() {

   print("timer start")
    
}
RxSwift写法:
var timer: Observable<Int>!

func setupTimer() {
    timer = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
    timer.subscribe(onNext: { (num) in
        print(num)
    })
    .disposed(by: disposeBag)
}

这里timer更好地是不用考虑页面上的Scrollviewtimer的影响

看完这些小例子🌰之后,觉得RxSwift比一般的Swift写法要简单好多,简直就是“骚一般的操作”。

4.RxSwift核心逻辑初识

在前面的概念介绍中大概已经知道了ReactiveX是基于异步 Event序列的响应式编程, 并提供更优雅的数据绑定,可以时刻响应新的数据同时顺序地处理它们,既然要响应数据,就还需要一个观察者。

RxSwift 核心概念就可以理解为一个观察者(Observer)订阅一个可被观察序列(Observable)。观察者对可被观察序列发射的事件(Event)或事件序列作出响应。

先来理解一下这三个类的概念:

  • Observable<T> 这个类就是 Rx 框架的基础,我们可以称它为可观察序列。它的作用就是可以异步地产生一系列的 Event(事件),即一个 Observable<T> 对象会随着时间推移不定期地发出 event(element : T) 这样一个东西。
  • 而且这些 Event 还可以携带数据,它的泛型 <T> 就是用来指定这个 Event 携带的数据的类型。
  • 有了可观察序列,我们还需要有一个 Observer(订阅者)来订阅它,这样这个订阅者才能收到 Observable<T> 不时发出的 Event

既然Observable是一个可被观察的序列,可以异步地产生一系列的 Event,那么查看RxSwift/Event.swift 源码可以发现

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被定义成一个枚举值,这也就是说一个Observable可观察序列可以发出三种不同类型的Event事件:

  • nextnext 事件就是那个可以携带数据 <T> 的事件,可以说它就是一个普通事件

  • errorerror 事件表示一个错误,它可以携带具体的错误内容,一旦 Observable 发出了 error event,则这个Observable就等于终止了,以后它再也不会发出event` 事件了。

  • completedcompleted事件表示 Observable 发出的事件正常地结束了,跟 error 一样,一旦 Observable 发出了 completed event,则这个 Observable 就等于终止了,以后它再也不会发出 event 事件了

序列监听有三个步骤:1.创建序列,2订阅序列,3.发送信号。当创建序列,并订阅了序列后,只要某个事件发送了序列消息,就可以在序列订阅的闭包里面监听到发送的消息。

下面创建一个可观察序列来感受一下:

//第一步:创建序列
//在create()函数中传入一个闭包,任务是对每一个过来的订阅进行处理
 let ob = Observable<Any>.create { (observer) -> Disposable in
            // 第三步:发送信号(onCompleted和onError只能发送一个)
            observer.onNext("你好啊")
            observer.onCompleted()
//            observer.onError(NSError.init(domain: "loser", code: 10010, userInfo: nil))
            return Disposables.create()
 
//第二步:订阅信息
//当我们订阅了Observable的消息后,只要Observable的事件触发,都会通过onNext这个闭包告诉我们。
 let _ = ob.subscribe(onNext: { (text) in
            print("订阅到:\(text)")    //这里会监听到订阅的Observable事件
        }, onError: { (error) in
            print("error: \(error)")    //当发生错误时,会回调这里
        }, onCompleted: { // 当序列执行完毕时,会回调这里。
            print("完成")
        }) {
            print("销毁") 
        }
        .disposed(by: disposeBag)

DisposeBag:作用是 Rx 在视图控制器或者其持有者将要销毁的时候,自动释法掉绑定在它上面的资源。它是通过类似“订阅处置机制”方式实现(类似于 NotificationCenterremoveObserver)。

总结

这就算是RxSwift响应式的核心逻辑了,这里有点疑惑就是:为什么观察者发出的信号,可观察序列能够订阅到呢?

未完待续...... RxSwift学习--核心逻辑再探

注:感谢下面的参考文档

RxSwift 中文文档

航歌Swift