RxSwift是一种函数式响应式编程。那什么是函数式编程呢,函数式编程最重要的概念就是“无状态(immutable)”,看到这有些小伙伴可能会很开心,无状态(知名LOL职业选手)嘛,我是他的粉丝!言归正传,到底什么是“无状态(immutable)”呢?我看了不少文章,可是都被他们专业的描述整的一头雾水,我来讲说个人见解:有丰富debug经验的小伙伴们都知道一个事实,程序中80%的BUG均可以由追踪变量的值来发现,为何呢?反过来想,引发BUG的最大元凶正是值的改变。说道这里你们确定都有了一个大胆的想法,假若有一种方式可以消除状态的改变(这里为何不说“值”,由于Rx不只仅是值的“无状态html
(immutable)”,也会有事件的“无状态(immutable)”,因此准确的描述应该是“状态”),那么岂不是就没什么BUG了?函数式编程正是为此而生。react
那函数式编程到底是怎么作到“无状态(immutable)”这一点的呢?答案就是“序列(sequence)”,既然我不能改变状态,那我须要发生改变的时候,我就从新生成一个状态,发生几回改变,我就生成几个状态,而“序列(sequence)”就是盛放这些状态的容器(你们能够看作是一个数组),那么状态的改变是否是就不复存在?答案是确定的,我只须要分别对“序列(sequence)”中的状态作出命令,他们之间就不会互相影响,那么出bug的概率天然大大下降。面试
那么既然成为了一个“序列”,怎么生成“序列”,怎么组合多个“序列”,怎么处理每一个“序列”中的每一个元素?RxSwift中有很是多的操做符来解决这些问题,这也是RxSwift难以学习的缘由之一。可是咱们只要理解,这些操做符的的最终目标是让“序列”变成咱们想要的样子。记忆起来就会事半功倍。 说完函数式,再来讲说响应式编程,先看段代码:编程
var a: Int = 0
var b: Int = 0
var c = a + b
a = 1
print(c)
复制代码
尽管a在c打印以前赋值为1,可是在给c赋值的时候 a + b 仍是等于 0 ,swift
咱们要想让c获得最新的结果,就须要在c赋值以前将a的值改成1,数组
那么若是a和b的值在以后的代码中会一直变更,promise
就须要不断的给c写一行从新赋值的代码:c = a+ b,这真是太麻烦了,markdown
响应式就是为了解决这个麻烦诞生的,当a或者b发生变化的时候,网络
c的值会响应a或者b的值发生变化,因此就被称之为响应式了,那么响应式如何实现呢,闭包
最简单容易理解的方式就是经过a或者b的属性监听器,当监听到a或者b发生变化时,
就从新给c赋值。实际上,不管代码怎么写,无非是隐式了c = a + b这一行代码而已,
即便咱们直接去操做指针,也须要有一行代码将两个指针指向的值相加,
因此响应式并非难以理解的技术,可是响应式编程可以极大的改善代码的可读性,
更直观,更容易维护。
RxSwift,就是将函数式,响应式,以及链式编程的结合体,即便在项目中因为各类缘由不能去使用,读懂它,也能让你在写业务代码的时候更加游刃有余。
在这里再多说几句:有不少博客把“序列(sequence)”比做“流、信号、管道”等,这确实有助于你们理解RxSwift,可是RxSwift的项目维护者显然不这么认为,下面给你们看一段InfoQ对项目维护者Krunoslav Zaher的采访:
使用不同的、不太准确的方式思考可观察的序列会带来不少问题(流、信号、导管等)。那些东西有时候能够用来帮助新手探索Rx,可是若是使用的时间过长,它们只会在接下来的开发中带来不少困惑。人们有时候使用它们是由于Rx能够用在模拟系统的多状态部分,可是那只是故事的一部分,而我建议你们不要那样思考。
使用那些项(流、信号、导管等)思考的问题在于它们带有一个隐含假定:
定义Observable时带有共享的值,而且能够从外部设定的当前值也是同样的。而Observable会被莫名其妙地直接取消,这看起来像future或promise。
而事实上:
Observable只是一个如何计算序列的定义。
没有计算会在序列定义后(let result = source.map(transformObservableSequence))自动地执行。这和Swift中的SequenceSequence是同样的。当绑定一个观察者时(这与调用SequenceType中的generate方法相同)才会执行计算,
而且能够经过处理Disposable对象的结果来取消特定的计算。
Observable固然能够表明多状态的计算,它们共享潜在的绑定并source可观察的序列(使用shareReplay(1)操做等),可是这不是默认的行为。
我认为部分的问题在于也许人们一直在使用future、promise或其余更简单的、使用方式类似的reactive库,因此他们天然地认为Rx在其余的状况下也是一样的表现,而这对于新手来讲显然是使人困惑的。
奇怪的是那些单方面的属性对刚开始使用Rx的人们形成了很大的问题,有时候门槛会变得过高以致于人们变的没有动力,可是从另外一角度来看,这正是我我的认为Rx美的缘由。
它美的地方在于你能够只经过一句话就能教会一我的使用Rx:Observable表明了元素类型为T的可观察序列(与Swift中的SequenceSequence是同样的),其中每一个元素均可以调用subscribe(和SequenceSequence中的generate方法相同)方法来注册本身的接受序列元素的observer(返回信号)。
RxSwift官方demo解析 下面是官网demo的一个小模块,实现了Github的注册功能,咱们先看一下目录结构(没有标绿的文件是RxSwift专门为UI设计的更为精简的实现方式,这里就很少作探讨):
显然这是一个标准的MVVM架构,在我看来RxSwift是MVVM的完美CP,RxSwift很是适合用来解决MVVM中,ViewModel的State和View绑定的问题。再扯一些题外话,demo中使用到了面向协议编程的思想,也是很值得你们参考的,可以大幅度提升代码的复用率和解耦合。推荐你们在慕课网上搜索2016swift开发者大会上,李洁信的演讲,在这里我就不附上地址了。
在正常状况下,完成2个密码的比对功能咱们须要在textField的代理方法中监听text属性的变化,而后使用相似于这种结构的判断:
if validPassWord(textfield.text) {
tipLabel1.text = ...
tipLabel.textColor = ...
} else {
code
}
if textfield1.text == textfield2.text {
tipLabel2.text = ...
tipLabel2.textColor = ...
}else {
code
}
复制代码
事实上,实际状况远比个人伪代码复杂的多得多,你们应该都深有体会。那么RxSwift又是怎么完成这些功能的呢?咱们先看一下viewController中的代码(为了方便你们理解我会添加注释):
override func viewDidLoad() {
super.viewDidLoad()
// 这里是viewModel的构造函数,构造函数会接收三个TextField中的text生成的“序列”
// 只要textField.text发生改变,“序列”中就会有一个新的元素Element生成
let viewModel = GithubSignupViewModel1(
input: (
// .rx.text.orEmpty.asObservable()是RxSwift提供的UIKit的扩展,能够快速生成一个可观察序列,如下同理
username: usernameOutlet.rx.text.orEmpty.asObservable(),
password: passwordOutlet.rx.text.orEmpty.asObservable(),
repeatedPassword: repeatedPasswordOutlet.rx.text.orEmpty.asObservable(),
loginTaps: signupOutlet.rx.tap.asObservable()
),
dependency: (
// 网络接口
API: GitHubDefaultAPI.sharedAPI,
// 验证输入内容是否合法的工具类
validationService: GitHubDefaultValidationService.sharedValidationService,
// 弹窗,点击登陆按钮后提示信息
wireframe: DefaultWireframe.shared
)
)
// viewModel构造函数中会处理出入的三个“序列”,而后生成如下五个可观察“序列”
// let validatedUsername: Observable<ValidationResult> // 用户名是否可用
// let validatedPassword: Observable<ValidationResult> // 密码是否可用
// let validatedPasswordRepeated: Observable<ValidationResult> // 重复密码是否可用
// let signupEnabled: Observable<Bool> // 注册按钮是否能够点击
// let signedIn: Observable<Bool> // 是否玩成了注册(控制弹窗)
// let signingIn: Observable<Bool> // 是否在注册进程中(控制菊花的旋转)
// ValidationResult是一个枚举
enum ValidationResult {
case ok(message: String)
case empty
case validating
case failed(message: String)
}
// 有了以上这些序列,就能够在控制器中完成数据和控件的绑定
viewModel.signupEnabled
// 订阅一个可观察序列的事件处理闭包,只要有新的元素添加到序列中,该方法就会自动发出信号并执行闭包中的代码,并返回一个遵照Disposable的类型,Disposable就至关于垃圾回收,会自动销毁序列。
.subscribe(onNext: { [weak self] valid in
self?.signupOutlet.isEnabled = valid
self?.signupOutlet.alpha = valid ? 1.0 : 0.5
})
.disposed(by: disposeBag)
viewModel.validatedUsername
/**
bind其实跟subscribe是等价的,只是他能更好的反应可观察序列和观察者之间的关系
站在一个代码阅读者的角度,用subscribe方法订阅,你可能须要在事件处理序列中去寻找谁是观察者,可是使用bind,你
能够很直观的看到观察者是谁,其实就是至关于对subscribe再次封装一层,让观察者在本身的代码区域完成事
件,就不用再写在控制器中,代码阅读起来真叫一个流畅,有一种一目了然的感受。也让你不知不觉中就下降了
代码的耦合度。
*/
.bind(to: usernameValidationOutlet.rx.validationResult)
.disposed(by: disposeBag)
viewModel.validatedPassword
.bind(to: passwordValidationOutlet.rx.validationResult)
.disposed(by: disposeBag)
viewModel.validatedPasswordRepeated
.bind(to: repeatedPasswordValidationOutlet.rx.validationResult)
.disposed(by: disposeBag)
viewModel.signingIn
.bind(to: signingUpOulet.rx.isAnimating)
.disposed(by: disposeBag)
viewModel.signedIn
.subscribe(onNext: { signedIn in
print("User signed in \(signedIn)")
})
.disposed(by: disposeBag)
//}
let tapBackground = UITapGestureRecognizer()
tapBackground.rx.event
.subscribe(onNext: { [weak self] _ in
self?.view.endEditing(true)
})
.disposed(by: disposeBag)
view.addGestureRecognizer(tapBackground)
}
复制代码
原本很想详细的解构一下官方demo中的代码解构,
真的是十分优秀简洁的代码,而后再说一说每一个操做符的含义,想让尚未开始接触到RxSwift的小伙伴稍稍感觉一下RxSwift的魅力所在。
在严峻的iOS市场中竞争, 没有掌握和了解一些具备优点的技术性知识,你怎么与别人进行PK,又怎么征服你的面试官。作过三五年的也未必必定会涉足到里面的技术性知识。在找工做中,技术足了,找不到工做也许你缺乏的是一份总结和一份面试题去练习。
[最新面试大厂常问面试答案一份和iOS技术资源 ]。但愿真心可以帮助到你们提高技术!
若是你由于本文稍稍对RxSwift提起兴趣,并打算学学看,那么下面这些博文千万不要错过: RxSwift函数响应式编程 RxSwift给Swift带来了原生Reactive编程的功能 是时候学习RxSwift了 [翻译]RxSwift入门
但愿读者能指出本文中出现的错误,不胜感激。
关注下面的标签,发现更多类似文章