美文网首页小食
RxSwift 源码解析01:RxSwift初体验

RxSwift 源码解析01:RxSwift初体验

作者: Style_月月 | 来源:发表于2022-08-23 22:06 被阅读0次

    本文主要是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<UITouch>, 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<UITouch>, 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<Int> = Observable<Int>.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<String> {
            return Observable<String>.create{ observer in
                observer.onNext("1")
                return Disposables.create()
            }
        }
    
        /// 通过 token 取得用户信息
        static func userInfo(token: String) -> Observable<String> {
            return Observable<String>.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<String> {
            return Observable<String>.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<ReactiveBase>.Type { get set }
    
        /// Reactive extensions.
        var rx: Reactive<ReactiveBase> { get set }
    }
    
    //协议的扩展
    extension ReactiveCompatible {
        /// Reactive extensions.
        public static var rx: Reactive<Self>.Type {
            get { Reactive<Self>.self }
            // this enables using Reactive to "mutate" base type
            // swiftlint:disable:next unused_setter_value
            set { }
        }
    
        /// Reactive extensions.
        public var rx: Reactive<Self> {
            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 源码解析01:RxSwift初体验

        本文链接:https://www.haomeiwen.com/subject/duxngrtx.html