一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。
主要记录Rxswift学习,之前也断断续续学习过。本系列记录下rxswift
系统的学习,虽然网上也有很多文章,这里只是加强下自己的记忆和理解。
1. RxSwift的理解
最开始接触oc还是学习的ReactiveCocoa
,也就是RAC
,也是学习MVVM
架构时学习的。对于RxSwift 他是rx系列
中swift的一个框架。我们知道swift
是一门静态语音,相比较oc来说没有运行时的机制。因此对于数据的响应,传递有一定的影响,因此需要我们使用一些辅助功能,因此rxswift
进行弥补。
1.1 RxSwift的优点
- 复合: Rx就是复合的代名词
- 复用: 复用性比较强,降低代码量
- 清晰: 因为声明都是不可变,代码函数式编程可读性强
- 易用: 理解容易,还抽象了异步编程,统一代码风格
- 稳定: 因为Rx是完全通过单元测试的
1.2 函数响应式编程
在学习RxSwift我们需要了解函数响应式编程思想,百度百科
对于我们之前使用oc是
面向对象
开发,
面向对象强调的是将与某数据类型
相关的一系列操作都封装到该数据
类型中去,因此,在数据类型中难免存在大量状态
,以及相关的行为
。虽然这很符合人类的逻辑直觉,但是当类型关系变得错综复杂
的时候,类型中状态的改变和类型之间彼此的继承和依赖
将使程序的复杂度几何上升
。
避免使用程序状态和可变对象
,是降低程序复杂度的有效方式之一,而这也正是函数式编程
的
精髓。函数式编程强调执行的结果
,而非执行的过程
。我们先构建一系列简单却具有一定功能
的小函数,然后再将这些函数进行组装
以实现完整的逻辑和复杂的运算,这是函数式编程的基本思想
以函数作为参数和返回值,我们使用代码演示下
// 函数式 - 数学 y = f(x) -> x = f(x) -> y = f(f(x))
// x 参数 2 = 1+1 = 0+2
// f 函数
// y 返回值
函数式是一种数学思想
,比如上面的列子我们可以把函数作为参数和返回值。
我们定义一个数组按照oc的思想进行for循环后不断添加判断条件,最终得到我们要的结果,可读性和清晰度比较差。
我们使用swift中的高阶函数
的话,可以发现可读性和清晰度都比较好。关于swift的高阶函数可以看我之前的介绍。我们同时修改也很简单
我们继续看下面的代码
// 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
吗?我们点击查看下
是一个拓展,遵循ReactiveCompatible
协议
里面有个关联类型associatedtype
,因此只要我们关联类型实现协议,都能实现rx
的set
和get
方法。
我们的NSObject
都实现了ReactiveCompatible
协议,因此我们 swift
中都可以使用rx
。
比如我们的textField的rx拓展,就是我们可以使用封装了我们平时事件和值。
3. 总结
我们简单的使用了解到RxSwift
通过对我们日常使用的UI控件
等的封装,他们都是继承了NSObject
,NSObject 遵循ReactiveCompatible协议
,我们swift中的类因此遵循协议,因此可以拓展实现rx
对应的实现。了通过函数式编程
的思想响应变化,是面向值编程
。我们通过创建序列,订阅,销毁
进行优雅,清晰的编程。