RxSwift 源码解析01:RxSwift初体验

本文主要是RxSwift Demo的体验

RxSwift 介绍

  • RxSwift = ReactiveX + Swift

    • ReactiveX(简称 Rx):是一个可以帮助我们简化异步编程的框架
    • Swift:表示是Rx的swift版本
  • RxSwift 作为一个响应式编程框架,有以下两点好处:

    • 聚合逻辑:即通过RxSwift提供的各种操作符实现逻辑的聚合
    • 响应式编程:即将所有事件都描述成一个可监听的序列,并提供大量功能各异的操作符,通过声明式的语句来完成数据的获取,转换,结合及绑定

为什么要使用RxSwift?

  • 复合 - Rx 就是复合的代名词
  • 复用 - 因为它易复合
  • 清晰 - 因为声明都是不可变更的
  • 易用 - 因为它抽象了异步编程,使我们统一了代码风格
  • 稳定 - 因为 Rx 是完全通过单元测试的
  • 高大上 - 代码档次比原生高很多

如果对于响应式编程还不了解的同学,可以先阅读# iOS 底层原理37:链式编程 中对响应式编程的介绍

初体验

通过 Cocoapods 导入

    pod 'RxSwift', '6.5.0'
    pod 'RxCocoa', '6.5.0'

RxSwift 的使用步骤分为以下几步:

  • 创建序列(万物皆可 rx
  • 订阅信号
  • 发送信号
  • 输出结果

主要从以下几方面来体验

  • KVO
  • Target-Action:UIbutton
  • 代理:delegate
  • 手势
  • 通知:Notification
  • timer
  • 网络请求:URLSession
  • 多个任务间有依赖关系
  • 等待多个并发任务完成后处理结果

KVO

在Swift中实现 OC 的KVO

  • 传统实现

KVO 三部曲

  • 监听
  • 响应
  • 销毁
class Person: NSObject {
    //由于swift是静态语言,而KVO是运行时发生的,所以需要加 dynamic 关键字
    @objc dynamic var name: String = "CJL"
}
//1-设置监听
func  setupKVO(){
    self.person.addObserver(self, forKeyPath: "name", options: .new, context: nil)
}
//2-回调
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    print("响应")
    print(change as Any)
}
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
    print("来了")
    person.name = "\(person.name) 6"
    print(person.name)
}
//3-移除监听
deinit{
    self.removeObserver(self, forKeyPath: "name", context: nil)
}
  • Rx实现
func  setupKVO(){
    self.person.rx.observeWeakly(String.self, "name")
        .subscribe { value in
            print(value as Any)
        }
        .disposed(by: disposeBag)
    
}
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
    print("来了")
    person.name = "\(person.name) 6"
}

Target-Action

这里以 UIButton 点击事件为例

  • 传统实现
var button: UIButton = {
    let btn = UIButton(type: .custom)
    btn.frame = CGRect(x: 100, y: 100, width: 200, height: 100)
    btn.setTitle("按钮点击", for: .normal)
    btn.backgroundColor = UIColor.lightGray
    return btn
}()

func setupButton(){
    button.addTarget(self, action: #selector(didClickButton), for: .touchUpInside)
}
@objc func didClickButton(){
    print("点击按钮")
}
  • Rx实现
func setupButton(){
    button.rx.controlEvent(.touchUpInside)
        .subscribe { _ in
            print("点击事件")
        }
        .disposed(by: disposeBag)
}

代理

这里以 UITextFiledDelegate 代理方法为例

  • 传统实现
func textFieldDidChangeSelection(_ textField: UITextField) {
    print(textField.text)
}
  • Rx实现
func setupTextField(){
    textFiled.rx.text.orEmpty.changed
        .subscribe { text in
            print(text)
        }
        .disposed(by: disposeBag)
}

此时还可以将 TextField 跟 button进行绑定,将textfiled输入的文本作为 button的标题

func setupTextField(){
    //textFiled跟button绑定,输入的内容作为button的title
    textFiled.rx.text
        .bind(to: button.rx.title())
        .disposed(by: disposeBag)
}

手势

这里以 UITapGestureRecognizer 点击手势为例

  • 传统实现
func setupGestureRecongizer(){
    let tap = UITapGestureRecognizer()
    self.view.addGestureRecognizer(tap)
    self.view.isUserInteractionEnabled = true
    tap.addTarget(self, action: #selector(tapAction(_:)))
}
@objc func tapAction(_ tap: UITapGestureRecognizer){
    print(tap.view)
}
  • Rx实现
func setupGestureRecongizer(){
    let tap = UITapGestureRecognizer()
    self.view.addGestureRecognizer(tap)
    self.view.isUserInteractionEnabled = true

    tap.rx.event
        .subscribe { tap in
            print(tap.view)
        }
        .disposed(by: disposeBag)
    
}

通知

这里以 keyboardWillShowNotification(键盘弹起通知)为例

  • 传统实现
func setupNotification(){
    // 监听键盘弹出通知
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name:UIResponder.keyboardWillShowNotification,object: nil)
}
@objc func keyboardWillShow(_ noti: Notification){
    print("键盘弹起")
}
  • Rx实现
func setupNotification(){
    // 监听键盘弹出通知
    NotificationCenter.default.rx.notification(UIResponder.keyboardWillShowNotification)
            .subscribe { noti in
                print("键盘弹起")
            }
            .disposed(by: disposeBag)
}

timer

实现计时器功能

  • 传统实现

5s倒计时

func setupTimer(){
    var countDownNum = 5
    let countdownTimer = Timer(timeInterval: 1.0, repeats: true) { timer in
        if countDownNum == 0 {
              // 销毁计时器
            timer.invalidate()
            // countDownNum = 5
            print(">>> Timer has Stopped!")
        } else {
            print(">>> Countdown Number: \(countDownNum)")
            countDownNum -= 1
        }
    }
    // 设置宽容度
    countdownTimer.tolerance = 0.2
    // 添加到当前 RunLoop,mode为默认。
    RunLoop.current.add(countdownTimer, forMode: .default)
    // 开始计时
    countdownTimer.fire()
}
  • Rx实现

正向计时

注:RxSwift 中的 timer并不是我们常见的几种timer定义的,而是自定义的,即不断创建序列,发送信号来实现的定时器效果,这个后面篇章会详细讲解

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

网络请求

  • 传统实现
func setupNetwork() {
    let url = URL(string: "https://www.baidu.com")
    URLSession.shared.dataTask(with: url!) { data, response, error in
        print(String.init(data: data!, encoding: .utf8)!)
    }.resume()
}
  • Rx实现
func setupNetwork() {
    let url = URL(string: "https://www.baidu.com")
    URLSession.shared.rx.response(request: URLRequest(url: url))
        .subscribe { response, data in
            print(response)
        }
        .disposed(by: disposeBag)
}

多个任务间有依赖关系

通过用户名密码取得 Token 然后通过 Token 取得用户信息,

  • 传统实现
// 用回调的方式封装接口
enum API {
    /// 通过用户名密码取得一个 token
    static func token(username: String, password: String,
        success: (String) -> Void,
        failure: (Error) -> Void) {  }

    /// 通过 token 取得用户信息
    static func userinfo(token: String,
        success: (String) -> Void,
        failure: (Error) -> Void) { }
}
func setupTaskDependency(){
    /// 通过用户名和密码获取用户信息
    API.token(username: "111", password: "222") { token in
        API.userinfo(token: token) { userinfo in
            print("获取用户信息成功: \(userinfo)")
        } failure: { error in
            print("获取用户信息失败: \(error)")
        }

    } failure: { error in
        print("获取用户信息失败: \(error)")
    }
}
  • Rx实现

避免了回调地狱,使得代码易读、已维护

/// 用 Rx 封装接口
enum API {
    /// 通过用户名密码取得一个 token
    static func token(username: String, password: String) -> Observable {
        return Observable.create{ observer in
            observer.onNext("1")
            return Disposables.create()
        }
    }

    /// 通过 token 取得用户信息
    static func userInfo(token: String) -> Observable {
        return Observable.create{ observer in
            observer.onNext("2")
            return Disposables.create()
        }
    }
}
func setupTaskDependency(){
    API.token(username: "111", password: "222")
        .flatMapLatest(API.userInfo)
        .subscribe(onNext: { userInfo in
            print("获取用户信息成功: \(userInfo)")
        }, onError: { error in
            print("获取用户信息失败: \(error)")
        })
        .disposed(by: disposeBag)
}

等待多个并发任务完成后处理结果

需要将两个网络请求合并成一个

  • 传统实现
let queue1:DispatchQueue = DispatchQueue.init(label: "textQueue1")
let queue2:DispatchQueue = DispatchQueue.init(label: "textQueue2")
// 队列queue加入队列组
queue1.async (group: self.gropQueue){
    /*任务1*/
    print("获取老师信息接口")

}
queue2.async (group: self.gropQueue){
    /*任务2*/
    print("获取老师评论接口")
}
/*2个异步任务完成后执行界面处理操作*/
self.gropQueue.notify(queue: DispatchQueue.main) {
    /*界面处理*/
    print("统一处理")
}
  • Rx实现

用几行代码实现复杂的异步操作

/// 用 Rx 封装接口
enum API2 {
    /// 取得老师的详细信息
    static func teacher(teacherId: Int) -> Observable {
        return Observable.create{ observer in
            observer.onNext("取得老师的详细信息")
            return Disposables.create()
        }
    }

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

疑问点

从上面的实现看,主要关注以下方面:

  • 其他对象调用的 rx 到底是什么?为什么都可以调用 rx?
  • timer 的创建为什么跟其他对象有所不同?
  • Observable、Observer是什么?

其他对象调用的 rx 到底是什么?为什么都可以调用 rx?

  • 点击 rx 进入其源码实现
/// A type that has reactive extensions.
public protocol ReactiveCompatible {
    /// Extended type  关联属性
    associatedtype ReactiveBase

    /// Reactive extensions.
    static var rx: Reactive.Type { get set }

    /// Reactive extensions.
    var rx: Reactive { get set }
}

//协议的扩展
extension ReactiveCompatible {
    /// Reactive extensions.
    public static var rx: Reactive.Type {
        get { Reactive.self }
        // this enables using Reactive to "mutate" base type
        // swiftlint:disable:next unused_setter_value
        set { }
    }

    /// Reactive extensions.
    public var rx: Reactive {
        get { Reactive(self) }
        // this enables using Reactive to "mutate" base object
        // swiftlint:disable:next unused_setter_value
        set { }
    }
}

import Foundation

// NSObject 遵循了 rx 所在的协议
/// Extend NSObject with `rx` proxy.
extension NSObject: ReactiveCompatible { }

  • 从源码可以看出 rx 是 ReactiveCompatible 协议的属性,而NSObject 遵循了 ReactiveCompatible,而我们常说 万物皆对象,对象溯源到底最终都指向 NSObject(即 rx 是 NSObject的扩展),所以这里得出一个结论:万物即可 rx

其余问题我们将在下一节来进行一一探索

参考文档

  • RxSwift 中文文档

你可能感兴趣的:(RxSwift 源码解析01:RxSwift初体验)