RxSwit学习-01-初识RxSwift

668 阅读6分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情
主要记录Rxswift学习,之前也断断续续学习过。本系列记录下rxswift系统的学习,虽然网上也有很多文章,这里只是加强下自己的记忆和理解。

1. RxSwift的理解

最开始接触oc还是学习的ReactiveCocoa,也就是RAC,也是学习MVVM架构时学习的。对于RxSwift 他是rx系列中swift的一个框架。我们知道swift是一门静态语音,相比较oc来说没有运行时的机制。因此对于数据的响应,传递有一定的影响,因此需要我们使用一些辅助功能,因此rxswift进行弥补。

1.1 RxSwift的优点

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

1.2 函数响应式编程

在学习RxSwift我们需要了解函数响应式编程思想,百度百科

image.png 对于我们之前使用oc是面向对象开发,

面向对象强调的是将与某数据类型相关的一系列操作都封装到该数据类型中去,因此,在数据类型中难免存在大量状态,以及相关的行为。虽然这很符合人类的逻辑直觉,但是当类型关系变得错综复杂的时候,类型中状态的改变和类型之间彼此的继承和依赖将使程序的复杂度几何上升

避免使用程序状态和可变对象,是降低程序复杂度的有效方式之一,而这也正是函数式编程的 精髓。函数式编程强调执行的结果,而非执行的过程。我们先构建一系列简单却具有一定功能 的小函数,然后再将这些函数进行组装以实现完整的逻辑和复杂的运算,这是函数式编程的基本思想

以函数作为参数和返回值,我们使用代码演示下

// 函数式 - 数学 y = f(x) -> x = f(x) -> y = f(f(x))

// x 参数  2 = 1+1 = 0+2

// f 函数

// y 返回值

函数式是一种数学思想,比如上面的列子我们可以把函数作为参数和返回值。

image.png

我们定义一个数组按照oc的思想进行for循环后不断添加判断条件,最终得到我们要的结果,可读性和清晰度比较差。
我们使用swift中的高阶函数的话,可以发现可读性和清晰度都比较好。关于swift的高阶函数可以看我之前的介绍。我们同时修改也很简单

image.png

我们继续看下面的代码

      // FRP - F - R

        // Int a = 10

        // Int b = a + 10 == 关系

        // a += 10   == 20

        // b =? 30

    

这里我们主要是表达了一种关系,b始终是比a多10,如果a增加10,此时b并不是30.我们可以使用kvo实现。但是要添加观察者,响应回调,最后移除。三部曲,比如我们观察对象的名字

//添加
self.person.addObserver(self, forKeyPath: "name", options: .new, context: nil)

//改变回调
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来实现。

2. RxSwift常用使用

2.1 KVO

对于上面的kvo的实现我们直接使用RxSwift来实现

self.person.rx.observeWeakly(String.self, "name")

            .subscribe { (name) in

                print(name)

            }

            .disposed(by: disposeBag)

当person对象的name属性发生变化监测到就会打印,可以发现我们主要是3部分,首先是创建观察序列进行订阅最后销毁。当name发生改变的时候,就会回调订阅者,最后加入垃圾袋类似我们的自动释放池统一管理内存。

2.2 button

对于通常我们的button点击默认是touchUpOutside我们可以使用tap

self.button.rx.tap

            .subscribe(onNext: { () in

                print("点击来了")

            })

            .disposed(by: disposeBag)

对于我们一些其他的button事件可以指定controlEvent事件类型

self.button.rx.controlEvent(.touchUpOutside)

            .subscribe(onNext: { () in

                

                print("点击走了")

            })

            .disposed(by: disposeBag)

2.3 textfiled

监听textfield的值发生变化,不为nil的情况

self.textFiled.rx.text.orEmpty

            .subscribe(onNext: { (text) in

               print(text)

            })

            .disposed(by: disposeBag)

我们可以绑定到我们的button按钮或者label

self.textFiled.rx.text

            .bind(to: self.button.rx.title())

            .disposed(by: disposeBag)
            
 self.textFiled.rx.text.orEmpty

            .bind(to: self.label.rx.text)

            .disposed(by: disposeBag)

经常使用比较多的情况是输入框位数等判断按钮是否可点击

let usernameVaild = self.textFiled.rx.text.orEmpty

            .map { (text) -> Bool in

                return text.count >= 6

        }

        usernameVaild.bind(to: self.button.rx.isEnabled)

            .disposed(by: disposeBag)

通常情况下组合判断的,后面会简单的分析下登陆的流程。

2.4 UITapGestureRecognizer

let tap = UITapGestureRecognizer()

        self.label.addGestureRecognizer(tap)

        self.label.isUserInteractionEnabled = true

        tap.rx.event.subscribe(onNext: { (tap) in

            print(tap.view as Any)

        })

        .disposed(by: disposeBag)

给一个label添加手势,点击的时候打印当前手势所在的视图。

2.5 scrollView

        scrollView.rx.contentOffset

            .subscribe(onNext: { [weak self](content) in

                self?.view.backgroundColor = UIColor.init(red: content.y/255*0.8, green: content.y/255*0.6, blue: content.y/255*0.3, alpha: 1)

            })

        .disposed(by: disposeBag)

监听scrollView的偏移量,从而改变背景色。

2.6 notification

NotificationCenter.default.rx.notification(UIResponder.keyboardWillHideNotification)

            .subscribe(onNext: { (noti) in

                print(noti)

            })

        .disposed(by: disposeBag)

通知,比如监听键盘将要消失时,我们可以做出相应改变。

2.7 timer

var timer: Observable<Int>!
timer = Observable<Int>.interval(1, scheduler: MainScheduler.instance)

        timer.subscribe(onNext: { (num) in

            print(num)

        })

        .disposed(by: disposeBag)

这里我们定义一个Int类型的序列,每秒发送一次信号,我们订阅做出对应的操作。其中scheduler类似我们的线程,MainScheduler.instance表示主线程。

2.8 network

      let url = URL(string: "https://www.baidu.com")

      URLSession.shared.rx.response(request: URLRequest(url: url!))

        .subscribe(onNext: { (response,data) in

            print(response)

        }).disposed(by: disposeBag)

对于我们系统的网络请求RxSwift也做出了对应的封装,不过我们通常会使用三方的框架Moya

2.9 小结

我们可以发现我们对于swift中,都是基于rx进行拓展的。那么rx是什么呢?类似我们OC中的NSObject吗?我们点击查看下

image.png

是一个拓展,遵循ReactiveCompatible协议

image.png

里面有个关联类型associatedtype,因此只要我们关联类型实现协议,都能实现rxsetget方法。

image.png

我们的NSObject都实现了ReactiveCompatible协议,因此我们 swift中都可以使用rx

image.png

比如我们的textField的rx拓展,就是我们可以使用封装了我们平时事件和值。

3. 总结

我们简单的使用了解到RxSwift通过对我们日常使用的UI控件等的封装,他们都是继承了NSObject,NSObject 遵循ReactiveCompatible协议,我们swift中的类因此遵循协议,因此可以拓展实现rx 对应的实现。了通过函数式编程的思想响应变化,是面向值编程。我们通过创建序列,订阅,销毁进行优雅,清晰的编程。