RxSwift 介绍与简单使用
文章目录
- 一、 Rx 介绍
- 1、什么是Rx
- 2、RxSwift
- 3、RxCocoa
- 二、Rx 常见用法
- 1、给 button 添加点击事件(RxCocoa)
- 2、事件 + bind + combine
- 3、遵循代理并实现
- 4、闭包回调
- 5、通知
- 6、多任务依赖关系管理
- 7、多任务异步并行
- 三、DisposeBag(清除包)介绍:
- 四、函数式编程介绍
- 参考文献:
一、 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 inif enable {self?.loginButton.isEnabled = trueself?.loginButton.setTitle("can click", for: .normal)} else {self?.loginButton.isEnabled = falseself?.loginButton.setTitle("can not click", for: .normal)}})
// .bind(to: loginButton.rx.isEnabled).disposed(by: disposeBag)loginButton.rx.tap.subscribe { _ inprint("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 inprint("contentOffset: \(contentOffset)")}).disposed(by: disposeBag) // 每一个绑定是有生命周期的,并且这个绑定是可以被清除的。将每一个绑定的生命周期交给 disposeBag 管理,当 disposeBag 释放时,会自动清理 _disposables 数组中所有的绑定}
}
4、闭包回调
传统实现方案:
URLSession.shared.dataTask(with: URLRequest(url: url)) { (data, response, error) inguard 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 inprint("Data Task Success with count: \(data.count)")}, onError: { error inprint("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) inprint("Application Will Enter Foreground")}
}deinit {NotificationCenter.default.removeObserver(notificationObserver)
}
Rx 实现方式:
override func viewDidLoad() {super.viewDidLoad()NotificationCenter.default.rx.notification(.UIApplicationWillEnterForeground).subscribe(onNext: { (notification) inprint("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] _ inself?.loadUserInfo { [unowned self] _ inself?.loadRecommendsGoods { _ in// do nothing}}}
}
Rx 实现方式
func rx_login(userName: String, password: String) -> Observable<String> {let createSequence = Observable<String>.create { observer -> Disposable inDispatchQueue.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 inDispatchQueue.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 inDispatchQueue.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 inprint(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) inprint("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)") }}
参考文献:
1、swift 官方中文文档
RxSwift 介绍与简单使用相关推荐
- Matplotlib的介绍及简单操作
Matplotlib的介绍及简单操作 1 什么是Matplotlib 是专门用于开发2D图表(包括3D图表) 以渐进.交互式方式实现数据可视化 2 为什么要学习Matplotlib 可视化是在整个数据 ...
- (三)AJAX基本介绍和简单实例03
(三)AJAX基本介绍和简单实例03-----Ajax与数据库的动态应用 前台显示界面: 选择所有客户之后: 选择其中一个客户---杜森: Demo03.html代码 <html> < ...
- rabbitMQ消息队列 – 面板介绍及简单demo
首先rabbit安装好之后,运维会给一个控制面板. 默认账号密码为guest 登入以后可以看到具体界面. 在此鸣谢百度翻译给予的大力支持.. ###写一个简单的demo 编写之前..虽然说可以直接用底 ...
- Protobuf介绍及简单使用(上)
目录 Protobuf 介绍 优势 protobuf语法 Specifying Field Rules Data type Data name Number Protobuf注释 保留字段与标识符 s ...
- SQL Server中追踪器Trace的介绍和简单使用
原文:SQL Server中追踪器Trace的介绍和简单使用 一.What is Trace? 对于SQL Profiler这个工具相信大家都不是很陌生,没用过的朋友可以在SQL Server Man ...
- S3C2440移植linux3.4.2内核之内核框架介绍及简单修改
文章目录 uboot启动内核分析 简单配置内核 编译内核 设置机器ID 修改晶振 移植Linux3.4.2内核其他文章链接: S3C2440移植linux3.4.2内核之内核框架介绍及简单修改 S3C ...
- 新颖的自我介绍_简单新颖的自我介绍范文
简单新颖的自我介绍范文 简单新颖的自我介绍范文1 各位考官好,今天能够站在这里参加面试,有机会向各位考官请教和学习,我感到非常的荣幸.希望通过这次面试能够把自己展示给大家,希望大家记住我.我叫.... ...
- Quartz框架实现定时任务介绍及简单使用
https://blog.csdn.net/bicheng4769/article/details/81097305 Quartz框架介绍及简单使用 https://www.cnblogs.com/d ...
- Hive第一天——Hive介绍以及简单使用
Hive第二天--Hive介绍以及简单使用 自己的话:黑发不知勤学早,白首方悔读书迟 每天都要保持前进! 一.什么是Hive 数据库: mysql.oracle.sqlserver.DB2.sqlit ...
- Helm模板常用语法介绍与简单应用场景
Helm模板常用语法介绍与简单应用场景 文章目录 Helm模板常用语法介绍与简单应用场景 什么是Helm _help.tpl子模版 应用场景 预定义对象 关于变量 关键字及应用 函数 流程与控制 什么 ...
最新文章
- 巧妙共享Win7/Vista/XP文件夹权限
- C语言--static修饰函数
- 17.C#类型判断和重载决策(九章9.4)
- 从EMD、WMD到WRD:文本向量序列的相似度计算
- kafka to mysql_Flink : kafka to mysql example
- android系统相机自动录像,android 调用系统相机录像并保存
- 【汇总】numpy函数合集
- c++向量和数组的区别_Matlab入门基础知识(5)对数组的操作
- IDEA中Maven项目中界面右边的Maven Projects窗口找不到不出来
- eclipse打war包_jar包和war包的区别
- 阿里影业“云智开放平台”炼成记!
- sentinel 打包_SpringCloud Alibaba整合Sentinel
- 配置apache密码认证
- 5-9 第五天 微信 JS-SDK-使用微信官方的WEB调试工具
- 三段式状态机原理详细解释
- python自然语言处理 | 分析句子结构
- cannot load facet kotlin
- Spectral Clustering(谱聚类和其他)
- Facebook MySQL工程师吐槽MemSQL:MySQL比你们快无数倍
- 课程设计:经验以及答辩情况汇总
热门文章
- Lecture06:市场出清问题的鲁棒方法
- [分析力学]解题思路 - 拉格朗日方程
- 运筹学-2-单纯形法的矩阵计算
- 1553B总线通信协议
- UOJ 180【UR #12】实验室外的攻防战
- hht时频谱 matlab 乱序_用HHT求取信号的时频谱与边际谱——转
- 月下夜想曲200.6(攻略2)
- Android实现网络视频播放
- Entry name ‘res/drawable-xhdpi-v4/ic_launcher.png‘ collided
- 怎么让照片变年轻_PS高手让你变得更年轻