文章目录
一、 Rx 介绍
1、什么是Rx
Rx 是 ReactiveX 的缩写,简单来说就是基于异步 Event 序列的响应式编程。Rx 可以简化异步编程方法,并提供更优雅的数据绑定,让我们可以时刻响应新的数据的同时,顺序地处理他们。
2、RxSwift
在编程过程中,我们经常需要去检测某些值的变化(如:textfield 输入变化),然后进行相应的处理。RxSwift 的出现,让程序里的时间传递响应方法做到了统一,将之前常用的事件传递方法(ru delegate、notification、target-action等),全部替换成 Rx 的“信号链”方式。
在 MVVM 的开发模式下,可以通过 RxSwift 获得更加方便的数据绑定方法,让 MVVM 更加灵活轻便。
3、RxCocoa
RxCocoa 是 RxSwift 的一部分,主要是 UI 相关的 Rx 封装。RxCocoa 实现了很多组件的绑定,协助开发者把值和控件进行绑定,避免代码中产生大量的通知、代理、数据修改等代码。也可以监听 delegate,无需把控件创建和 delegate 处理分开。
二、Rx 常见用法
1、给 button 添加点击事件(RxCocoa)
在之前,当我们需要给button添加一个点击事件的时候,得这么干:
button.addTarget(self, action: #selector(buttonClicked), for: .touchUpInside)
@objc func buttonClicked() {
...
}
在使用 RxCocoa 之后,只需这样写:
button.rx.tap
.subscribe { _ in // 订阅点击事件信号
print("clicked button")
}
.disposed(by: disposeBag) //
// RxSwift tap 源码
extension Reactive where Base: UIButton {
/// Reactive wrapper for `TouchUpInside` control event.
public var tap: ControlEvent<Void> {
return controlEvent(.touchUpInside)
}
}
2、事件 + bind + combine
- share(replay: 1):共享同一个源,不单独创建新的源,以减少不必要的开支;
- orEmpty:将可选值的 nil 转化为空字符串输出,以减少可选类型隐式解包;
- bind:用来将一个信号发送者和一个信号监听者绑定在一起,即有信号发送,监听者自动收到通知;
- combine:信号融合
func rxCombine() {
let accountValid = accountTextField.rx.text.orEmpty
.map {
$0.count >= 5
}.share(replay: 1)
// 用 accountValid 来控制用户名提示语是否隐藏以及密码输入框是否可用。shareReplay 就是让他们共享这一个源,而不是为他们单独创建新的源。这样可以减少不必要的开支。
let passwordValid = passwordTextField.rx.text.orEmpty.map {
$0.count >= 5
}.share(replay: 1)
let everythingValid = Observable.combineLatest(accountValid, passwordValid) {
$0 && $1
}.share(replay: 1)
accountValid
.bind(to: passwordTextField.rx.isEnabled)
.disposed(by: disposeBag)
everythingValid
.bind(onNext: { [weak self] enable in
if enable {
self?.loginButton.isEnabled = true
self?.loginButton.setTitle("can click", for: .normal)
} else {
self?.loginButton.isEnabled = false
self?.loginButton.setTitle("can not click", for: .normal)
}
})
// .bind(to: loginButton.rx.isEnabled)
.disposed(by: disposeBag)
loginButton.rx.tap
.subscribe { _ in
print("click the login button")
}
.disposed(by: disposeBag)
}
3、遵循代理并实现
传统代理方法实现:
class ViewController: UIViewController {
...
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
}
}
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print("contentOffset: \(scrollView.contentOffset)")
}
}
使用 Rx 实现:
class ViewController: UIViewController {
...
override func viewDidLoad() {
super.viewDidLoad()
scrollView.rx.contentOffset
.subscribe(onNext: { contentOffset in
print("contentOffset: \(contentOffset)")
})
.disposed(by: disposeBag) // 每一个绑定是有生命周期的,并且这个绑定是可以被清除的。将每一个绑定的生命周期交给 disposeBag 管理,当 disposeBag 释放时,会自动清理 _disposables 数组中所有的绑定
}
}
4、闭包回调
传统实现方案:
URLSession.shared.dataTask(with: URLRequest(url: url)) { (data, response, error) in
guard error == nil else {
print("Data Task Error: \(error!)")
return
}
guard let data = data else {
print("Data Task Error: unknown")
return
}
print("Data Task Success with count: \(data.count)")
}.resume()
Rx 实现方案:
URLSession.shared.rx.data(request: URLRequest(url: url))
.subscribe(onNext: { data in
print("Data Task Success with count: \(data.count)")
}, onError: { error in
print("Data Task Error: \(error)")
})
.disposed(by: disposeBag)
通过 Rx 的方式,让回调变得非常的简单。
5、通知
var notificationObserver: NSObjectProtocol!
override func viewDidLoad() {
super.viewDidLoad()
notificationObserver = NotificationCenter.default.addObserver(forName: .UIApplicationWillEnterForeground, object: nil, queue: nil) { (notification) in
print("Application Will Enter Foreground")
}
}
deinit {
NotificationCenter.default.removeObserver(notificationObserver)
}
Rx 实现方式:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.rx
.notification(.UIApplicationWillEnterForeground)
.subscribe(onNext: { (notification) in
print("Application Will Enter Foreground")
})
.disposed(by: disposeBag)
}
6、多任务依赖关系管理
多任务依赖管理管理,如异步串行请求,由于业务原因,可能会存在请求依赖的场景,如:登录完成获取到token后才能请求用户信息。
func login(userName: String, password: String, completion: @escaping (_ result: Result<Any, Error>?) -> Void) {
DispatchQueue.global().async {
print("normal login success")
completion(nil)
}
}
func loadUserInfo(completion: @escaping (_ result: Result<Any, Error>?) -> Void) {
DispatchQueue.global().async {
print("normal load user info success")
completion(nil)
}
}
func loadRecommendsGoods(completion: @escaping (_ result: Result<Any, Error>?) -> Void) {
DispatchQueue.global().async {
print("normal load recommend goods success")
completion(nil)
}
}
// 嵌套调用,异步串行
func normal_taskStart() {
login(userName: "enoch", password: "11111") { [weak self] _ in
self?.loadUserInfo { [unowned self] _ in
self?.loadRecommendsGoods { _ in
// do nothing
}
}
}
}
Rx 实现方式
func rx_login(userName: String, password: String) -> Observable<String> {
let createSequence = Observable<String>.create { observer -> Disposable in
DispatchQueue.global().async {
print("rx login success")
observer.onNext("login success")
observer.onCompleted()
}
return Disposables.create()
}
return createSequence
}
func rx_loadUserInfo() -> Observable<[String : Any]> {
let createSequence = Observable<[String : Any]>.create { observer -> Disposable in
DispatchQueue.global().async {
print("rx load user info success")
observer.onNext(["name" : "enoch", "age" : 18])
observer.onCompleted()
}
return Disposables.create()
}
return createSequence
}
func rx_loadRecommendsGoods() -> Observable<[String]> {
let createSequence = Observable<[String]>.create { observer -> Disposable in
DispatchQueue.global().async {
print("rx load recommend goods success")
observer.onNext(["goods1", "goods2", "goods3"])
observer.onCompleted()
}
return Disposables.create()
}
return createSequence
}
// 异步串行调用
func rx_taskStart() {
rx_login(userName: "enoch", password: "111111")
.flatMap { [unowned self] _ in self.rx_loadUserInfo() }
.flatMap { [unowned self] _ in self.rx_loadRecommendsGoods() }
.subscribe(onNext: { goodsArray in
print(goodsArray)
})
.disposed(by: disposeBag)
}
7、多任务异步并行
Rx 当中,可使用压缩信号的方式,进行多任务异步并行,示例代码如下:
func rx_zipTask() {
Observable.zip(
rx_login(userName: "enoch", password: "111111"),
rx_loadUserInfo(),
rx_loadRecommendsGoods()
).subscribe(onNext: { (token, userData, goodsData) in
print("token:\(token)")
print("user data:\(userData)")
print("goods data:\(goodsData)")
}, onError: { error in
// do nothing
})
.disposed(by: disposeBag)
}
三、DisposeBag(清除包)介绍:
- DisposeBag 有一个专门存放垃圾回收的 _disposables 数组;和一个表示当前对象是否被回收的属性值_isDisposed (默认是false);
- 当 DisposeBag 的生命周期在 Viewcontroller 中结束的时候,调用自身的 deinit,随后调用 self.dispose();
- 紧接着 会将_isDisposed 变成ture,然后循环遍历 _disposables 数组,并将所有 Disposable对象都调用 dispose() 方法进行释放
- 每一个绑定是有生命周期的,并且这个绑定是可以被清除的。将每一个绑定的生命周期交给 disposeBag 管理,当 disposeBag 释放时,会自动清理 _disposables 数组中所有的绑定
四、函数式编程介绍
编程范式了解一下:
- 命令式:命令式编程通过一系列改变程序状态的指令来完成计算。命令式编程模拟电脑运算,是行动导向的,关键在于定义解法,即“怎么做”,因而算法是显性而目标是隐性的;
- 声明式:声明式编程只描述程序应该完成的任务。声明式编程模拟人脑思维,是目标驱动的,关键在于描述问题,即“做什么”,因而目标是显性而算法是隐性的;
函数式编程是指声明式范式编程,它需要我们将函数作为参数传递,或者作为返回值返还,我们可以通过组合不同的函数来得到想要的结果。
函数式编程优势:
- 减轻程序猿思考的负担,降低出错可能性,简称防秃;
- 代码可读性高;
- 代码更简洁;
- 适用于并发环境;
- 易于优化;
- 细粒度的重用(函数级别);
- 易于测试;
下方是简单举例:
func studentFilter() {
let studentsInGradeThreeClassThree = allStudents()
.filter { student -> Bool in student.grade == 3 && student.cls == 3 }
print("三年级三班有 \(studentsInGradeThreeClassThree.count) 人")
studentsInGradeThreeClassThree
.filter { student -> Bool in student.sex == .male }
.forEach { boy in boy.singASong() }
studentsInGradeThreeClassThree
.filter { student -> Bool in student.score > UInt(90) }
.forEach { student in print(student.father) }
studentsInGradeThreeClassThree
.sorted { (student1, student2) -> Bool in student1.score > student2.score }
.forEach { student in print("\(student.name): \(student.score)") }
}

本文介绍了RxSwift,一种用于响应式编程的库,简化了异步事件处理和数据绑定。内容包括RxSwift基本概念,RxCocoa对于UI交互的支持,以及如何使用RxSwift处理按钮点击、事件绑定、代理实现、闭包回调、通知、任务依赖管理和并行任务。同时,文章还探讨了DisposeBag在资源管理中的作用和函数式编程的优势。

3876

被折叠的 条评论
为什么被折叠?



