Rx 是 ReactiveX 的缩写,简单来说就是基于异步 Event 序列的响应式编程。Rx 可以简化异步编程方法,并提供更优雅的数据绑定,让我们可以时刻响应新的数据的同时,顺序地处理他们。
在编程过程中,我们经常需要去检测某些值的变化(如:textfield 输入变化),然后进行相应的处理。RxSwift 的出现,让程序里的时间传递响应方法做到了统一,将之前常用的事件传递方法(ru delegate、notification、target-action等),全部替换成 Rx 的“信号链”方式。
在 MVVM 的开发模式下,可以通过 RxSwift 获得更加方便的数据绑定方法,让 MVVM 更加灵活轻便。
RxCocoa 是 RxSwift 的一部分,主要是 UI 相关的 Rx 封装。RxCocoa 实现了很多组件的绑定,协助开发者把值和控件进行绑定,避免代码中产生大量的通知、代理、数据修改等代码。也可以监听 delegate,无需把控件创建和 delegate 处理分开。
在之前,当我们需要给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 {
return controlEvent(.touchUpInside)
}
}
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)
}
传统代理方法实现:
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 数组中所有的绑定
}
}
传统实现方案:
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 的方式,让回调变得非常的简单。
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)
}
多任务依赖管理管理,如异步串行请求,由于业务原因,可能会存在请求依赖的场景,如:登录完成获取到token后才能请求用户信息。
func login(userName: String, password: String, completion: @escaping (_ result: Result?) -> Void) {
DispatchQueue.global().async {
print("normal login success")
completion(nil)
}
}
func loadUserInfo(completion: @escaping (_ result: Result?) -> Void) {
DispatchQueue.global().async {
print("normal load user info success")
completion(nil)
}
}
func loadRecommendsGoods(completion: @escaping (_ result: Result?) -> 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 {
let createSequence = Observable.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)
}
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)
}
编程范式了解一下:
函数式编程是指声明式范式编程,它需要我们将函数作为参数传递,或者作为返回值返还,我们可以通过组合不同的函数来得到想要的结果。
函数式编程优势:
下方是简单举例:
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)") }
}
1、swift 官方中文文档