在iOS开发中,比较常用的响应式框架有两个.一个是OC版本的ReactiveCocoa,另一个是Swift版本的RxSwift:
ReactiveCocoa
简称RAC,有Objective-C , Swift版本ReactiveX
简称Rx,有众多编程语言版本,比如RxJava , RxKotlin , RxJS , RxCpp , RxPHP , RxgGo , RxSwift等等.
从上面可以看出Rx支持的语言种类更多,它的API和维护也更加完善,所以我们主要了解掌握Rx就好.
首先我们使用CocoaPods导入RxSwift:
pod 'RxSwift','~>5'
pod 'RxCocoa','~>5'
RxSwift : 是Rx标准API的Swift实现,不包括任何iOS相关的内容.
RxCocoa : 基于RxSwift,给iOS UI控件扩展了很多Rx特性.
RxSwift核心角色
Observable: 负责发送事件 (Event)Observer: 负责订阅Observable,监听Observable发送的事件 ( Event )
事件 ( Event )
RxSwift中的事件就是一个枚举类型,如下:
public enum Event<Element> {
/// Next element is produced.
case next(Element)
/// Sequence terminated with an error.
case error(Swift.Error)
/// Sequence completed successfully.
case completed
}
next : 携带的具体数据.
error : 携带错误信息,表明Observable终止,不会再发出事件.
completed : 表明Observable正常终止,不会再发出事件.
Observable相当于一个发射器,向外发送事件,我们看看怎么创建一个Observable:
创建Observable的五种方式
创建方式一: 使用create方法创建:
let observable = Observable<Int>.create { (observer) -> Disposable in
return Disposables.create()
}
create方法闭包参数要求返回一个Disposable实例所以在闭包中必须返回return Disposables.create(),这是一个固定写法.
创建好了Observable,怎么发送消息呢?
create的闭包参数中会传入一个AnyObserver参数,直接使用Observer发送事件( 数据 ):
let observable = Observable<Int>.create { (observer) -> Disposable in
//发送事件
observer.onNext(1)
observer.onNext(2)
observer.onNext(3)
observer.onNext(4)
return Disposables.create()
}
创建方式二: 发送单次消息
let observable2 = Observable.just(1)
使用just方式创建Observable传入事件,并且会单次发送消息,相当于:
let observable1 = Observable<Int>.create { (observer) -> Disposable in
//发送单次消息
observer.onNext(1)
observer.onCompleted()
return Disposables.create()
}
创建方式三: 发送多次消息
let observable3 = Observable.of(1,2,3,4)
创建方式四: 发送多次消息,数组形式
let observable4 = Observable.from([1,2,3,4])
创建方式五: 定时器
// 2s 之后,每隔1s,在主线程
let observable5 = Observable<Int>.timer(.seconds(5), period: .seconds(1), scheduler: MainScheduler.instance)
上面已经完成了事件/数据的发送,接下来就该订阅数据了.
订阅事件的方式有三种:
订阅方式一: 直接使用闭包表达式订阅:
observable.subscribe { event in
switch event{
case .next(let element):
print("next",element)
case .error(let error):
print("error",error)
case .completed:
print("completed")
}
// 打印效果
next 1
next 2
next 3
next 4
订阅方式二: 将事件拆分开:
observable.subscribe(onNext: { (element) in
print(element)
}, onError: { (error) in
print(error)
}, onCompleted: {
print("complement")
}, onDisposed: {
print("onDisposed")
})
订阅方式三: 通过Observer方式订阅:
//先创建一个 observer
let observer = AnyObserver<Int>.init { (element) in
switch element{
case .next(let element):
print("next",element)
case .error(let error):
print("error",error)
case .completed:
print("completed")
}
}
observable.subscribe(observer)
Observer除了AnyObserver这种类型外还有Binder类型.

现在我们创建Binder类型的Observer,并且实现订阅:
// 2s 之后,每隔1s,在主线程
let observable5 = Observable<Int>.timer(.seconds(2), period: .seconds(1), scheduler: MainScheduler.instance)
// 创建 observer
let bind = Binder<String>.init(label) { (label, str) in
label.text = str
}
//订阅消息
observable5.map{"\($0)"}.subscribe(bind)
//直接通过 bind:to 绑定 和 subscribe 等价
observable5.map{"数字是:\($0)"}.bind(to: label.rx.text)

内存管理
每当Observable被订阅时,都会返回一个Disposable实例,当调用Disposable的dispose时,就相当于取消订阅.
一共有三种方式可以取消订阅:
取消方式一: dispose()
调用Disposable的dispose()方法.
observable1.subscribe { (event) in
switch event{
case .next(let event):
print("nect : ", event)
case .error(let error):
print("error : ", error)
case .completed:
print("completed")
}
}.dispose()
上面直接调用dispose立马取消订阅,相当于只订阅一次.
也可以将dispose声明成一个属性,在需要取消订阅的时候取消.
var disposable: Disposable!
//订阅
disposable = observable1.subscribe { (event) in
switch event{
case .next(let event):
print("nect : ", event)
case .error(let error):
print("error : ", error)
case .completed:
print("completed")
}
}
//取消订阅
disposable.dispose()
取消方式二: bag
第二种方式是采用垃圾回收袋的方式bag
var bag = DisposeBag()
//订阅
observable1.subscribe { (event) in
switch event{
case .next(let event):
print("nect : ", event)
case .error(let error):
print("error : ", error)
case .completed:
print("completed")
}
}.disposed(by: bag)
采用bag方式意思是当bag销毁的时候,会自动将放入bag的Disposable的实例dispose.
取消方式三: takeUntil
observable1.takeUntil(self.rx.deallocated).subscribe { (event) in
switch event{
case .next(let event):
print("nect : ", event)
case .error(let error):
print("error : ", error)
case .completed:
print("completed")
}
}
until当self销毁( deinit )时,会自动调用Disposable实例的dispose.
takeUntil后面要求传入一个ObservableType.上面示例代码传入的是当前ViewController.为什么ViewController能调用出rx呢?是不是看的有些眼熟,这正是我们在面向协议编程 中讲的给系统类添加前缀的方法.
Binder
上面已经讲过Binder是另一种常用的Observer.其实Binder在响应式编程中经常被用到,特别是在绑定一些控件时.
比如倒计时功能:
var disposable: Disposable!
disposable = observable3.map({ num in
print(num)
if num == 11 {
self.disposable.dispose()
}
if num == 10 {
return "重试"
}
return "\(self.time - num)"
}).bind(to: bind3)
上面代码从10倒计时到0时取消订阅,并且label字体更改为重试.
我们可以对上面的代码进行优化,我们知道.bind()后面跟的是一个ObserverType类型.而ObserverType有两种:AnyObserver和Binder.

现在我们的目的是把倒计时的数字和Label的text绑定起来,所以我们下手点是扩展一个ObserverType类型的label.text.
这就用到面向协议编程的概念了.
首先我们知道RxCocoa已经对所有的 iOS UI控件扩展了很多Rx特性.
比如UILable就能调用rx:

可以看到rx是Reactive<>类型,所以我们要给Reactive扩展功能:
//为Reactive扩展功能
extension Reactive where Base: UILabel{
//扩展一个类型为 Binder<String> 的 myText 计算属性
var myText: Binder<String>{
//在 Binder 的 init 方法内部,直接把监听到的值赋值给 label.text
Binder<String>.init(base) { (base, str) in
base.text = str
}
}
}
扩展完成后就能直接绑定,不需要再单独创建Binder了:
//优化倒计时,
let observable3 = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
disposable = observable3.map({ num in
print(num)
if num == 11 {
self.disposable.dispose()
}
if num == 10 {
return "重试"
}
return "\(self.time - num)"
}).bind(to: label.rx.myText)
其实我们自己为UILabel扩展的myText属性,Rx早就为我们扩展好了:

不但如此,Rx几乎把所有UI控件的属性都扩展了:

在iOS开发中经常要对各种状态进行监听,常见的监听方案有:
KVOTarget-ActionNotificationDelegateBlock 回调
我们学习了Rx后,就多了一种监听方案,比如用Rx替换Target-Action.
常规的Target-Action做法:
button.addTarget(self, action: #selector(test), for: .touchUpInside)
@objc func test(){
print("按钮被点击了")
}
使用Rx监听按钮状态:
let observable = button.rx.controlEvent(.touchUpInside)
observable.subscribe(onNext: {
print("按钮被点击了")
})
Rx实现KVO:
class Student: NSObject {
@objc dynamic var name: String?
}
var xiaoMing = Student()
xiaoMing.rx.observe(String.self, "name").subscribe(onNext: { (name) in
print("name is ",name ?? "nil")
})
xiaoMing.name = "Jack"
xiaoMing.name = "Tom"
Rx实现通知:
NotificationCenter.default.rx.notification(UIApplication.didEnterBackgroundNotification).subscribe(onNext: {(notificaton) in
print("程序进入后台",notificaton)
})
既是Observable又是Observer
在Rx中有些对象既被当作Observable使用,也可当做Observer使用,比如Slider:


slider.rx.value是ControlProperty类型:

而ControlProperty又遵守了ObservableType, ObserverType协议:

所以ControlProperty既可当Observable也可当Observer使用.
textField.rx.text也是ControlProperty类型,我们要想监听textField文字改变就简单很多:
//监听 textField 文字改变
textField.rx.text.subscribe(onNext: { (text) in
print(text)
}).disposed(by: bag)
Rx配合TableView使用:
// 数据就是 Observable
var students = Observable.just([
Student(name: "张三", age: 18),
Student(name: "李四", age: 19),
Student(name: "王五", age: 20)
])
// 把数据绑定到 cell 上, Rx 把 cell 封装成了 items
students.bind(to: tableView.rx.items(cellIdentifier: "cell")){
row, stu, cell in
cell.textLabel?.text = stu.name
cell.detailTextLabel?.text = "\(stu.age)"
}.disposed(by: bag)
//监听 tableView 点击, 回传 点击的具体对象
tableView.rx.modelSelected(Student.self).subscribe(onNext: { (student) in
print(student)
})
//监听 tableView 点击, 回传 点击的 indexPath
tableView.rx.itemSelected.subscribe(onNext: { (indexPath) in
print(indexPath)
})