【RxSwift】RxSwift的理论知识

目录
一、RxSwift是什么,为什么要使用RxSwift
二、RxSwift的核心角色
 1、Event
 2、Observable
 3、Observer要做的处理
 4、Observer
三、Observer监听Observable,以及监听行为的释放
 1、Observer监听Observable
 2、监听行为的释放
四、Subjects


一、RxSwift是什么,为什么要使用RxSwift


RxSwift是响应式编程的一个开源框架,所谓响应式编程(Reactive Programming,简称RP)就是一种编程范式——即怎么写代码的方法论。

1️⃣如果项目里你用的是MVVM架构的话,响应式编程则提供了更加简单优雅的方式来做ViewModel和View的双向绑定,从而使得你的MVVM更加如虎添翼;2️⃣传统方式中我们经常使用代理、block、通知等方式来进行事件传递,它们各有各的写法,而响应式编程则可以让事件传递的方式做到统一——链式调用,同时能降低代码的耦合度提高代码内聚度(事件里通常会携带数据,所以可以直接把事件看成是数据、把事件传递看成是数据传递),当然响应式编程还有很多其它方面的用途你可以去挖掘,比如在网络请求方面的应用、在手势方面的应用、在多个异步任务处理方面的应用等。

比较出名和成熟的响应式编程框架有:

  • ReactiveCocoa,简称RAC,有OC和Swift版本
  • ReactiveX,简称Rx,有众多编程语言的版本,如RxJava、RxKotlin、RxSwift、RxDart等,它们的API基本都是一样的,只不过是基于不同语言实现的而已,但是没有OC版本,不过如果真得要用OC开发,我们也可以OC和Swift混编来调用RxSwift

Rx有这么多编程语言的版本,足以说明大家都比较认可它的那一套机制,社区肯定要比RAC活跃得多,因此如果要选择一个响应式编程框架,推荐Rx。

项目里要想使用RxSwift,得先安装RxSwift和RxCocoa这两个框架,这里只演示使用CocoaPods的安装方式,像使用其它三方库一样直接在Podfile文件里依赖这两个框架,执行pod install即可。

pod 'RxSwift', '~> 5.0'
pod 'RxCocoa', '~> 5.0'

然后在想使用RxSwift的地方导入这两个框架。

// RxSwift是Rx标准API的Swift版本实现,不包含任何iOS相关的内容
import RxSwift
// RxCocoa则是基于RxSwift给iOS的UI控件扩展了很多Rx特性
import RxCocoa


二、RxSwift的核心角色


iOS开发中的KVO、通知都是观察者设计模式,在设计模式中它可是一种重中之重的设计模式。比如宝宝在睡觉,爸爸妈妈总不能一直在旁边看着吧,那样就太累了,他们该做啥事做啥事,只要听到宝宝的哭声,给宝宝喂奶就行了,这就是一个典型的观察者设计模式。在这个例子里,宝宝是被观察者,爸爸妈妈是观察者,只要被观察者发出了事件——哭声,观察者收到后就会做出相应的处理——喂奶。据此我们可以得到观察者设计模式的核心角色:

  • 被观察者(也叫发布者)
  • 被观察者要发出的事件
  • 观察者(也叫订阅者)
  • 观察者要做的处理

RxSwift跟观察者设计模式很像,核心角色也是上面这四个。

1、Event

Event就是这么一个泛型枚举:

public enum Event<Element> {
    case next(Element)
    case error(Swift.Error)
    case completed
}

这个泛型枚举一共有三个值,所以事件一共有三种类型,也就是说Observable只可能发出这三种类型的事件:

  • next事件:一个携带正常数据的正常事件,正常数据的数据类型就是这个泛型Element、它可以是任意类型、不过早在Observable那儿就指定好了,正常数据会被挂在事件的element属性上
  • error事件:一个携带错误信息的错误事件,错误信息的数据类型为Error类型,错误信息会被挂在事件的error属性上
// Error其实是一个协议,要想发出error事件,我们就得自定义Error
enum CustomError: Error {
    case error1
    case error2
    case error3
}
  • completed事件:一个什么数据都不携带的完成事件

Event就是事件,它就是Observable随着时间的推移源源不断发出的东西。事件的作用就是作为数据的载体来携带数据,next事件就是正常数据的载体,error事件就是错误信息的载体,也就是说Observable要想发送一个正常数据就发出一个next事件,Observable要想发送一个错误信息就发出一个error事件,需要注意的是Observable一旦发出error事件就代表它因错误而终止了,以后再也不会发出事件。completed事件虽然不是数据的载体,但它也是一个不可或缺的事件,Observable一旦发出completed事件就代表它正常终止了,以后再也不会发出事件。

2、Observable

Observable就是这么一个泛型类:

// 注意:类型为ObservableType
public class Observable<Element> : ObservableType {
    ...
}

Observable就是被观察者,它其实是一个事件序列(Event Sequence),所以我们通常又称它为被观察序列。Observable的作用就是随着时间的推移源源不断地发出事件,你想它发出的事件挂什么数据类型的数据就把它的泛型Element搞成什么数据类型就行了。

  • 创建Observable及Observable发出事件
// 1、怎么创建一个observable?
//
// 调用Observable的类方法create就可以创建一个observable,假设这个observable发出的事件挂的数据为Int类型
//
// create方法的入参是一个闭包,这个闭包的入参是一个observer——这个observer就是将来监听该observable的observer,这个闭包的返回值是一个disposable
// create方法的返回值是一个observable
let observable = Observable<Int>.create { observer in
    // 2、observable怎么发出事件?
    //
    // 这个闭包属于observable,所以在这个环境里:
    // observable给observer发出next事件,换句话说就是给observer发送一个next消息(消息机制),即让observer调用一下它的onNext方法
    // observable给observer发出completed事件或error事件,同理
    observer.onNext(1)
    observer.onNext(2)
    observer.onNext(3)
    observer.onCompleted()
//    observer.onError(CustomError.error1)
    
    // 因为observable一旦发出completed事件或error事件就终止了,所以后面这三个事件是发不出去的
    observer.onNext(4)
    observer.onNext(5)
    observer.onNext(6)
    
    return Disposables.create()
}
  • 快捷创建Observable及Observable发出事件
// 单个事件Observable:该Observable只能发出一个next事件 + 一个completed事件
//
// 1、调用Observable的类方法just就可以快捷创建一个单个事件observable,可以省略泛型,直接传你想发送的数据就行,会自动给你泛好
// 2、创建好、被监听后,该Observable就会自动发出事件
let justObservable = Observable.just(1)

// 等价于
let observable = Observable<Int>.create { observer in
    observer.onNext(1)
    observer.onCompleted()
    return Disposables.create()
}
// 多个事件Observable:该Observable能发出多个next事件 + 一个completed事件
//
// 1、调用Observable的类方法from或of就可以快捷创建一个多个事件observable,可以省略泛型,直接传你想发送的数据就行,会自动给你泛好
// 2、创建好、被监听后,该Observable就会自动发出事件
let fromObservable = Observable.from([1, 2, 3]) // 通过数组来生成多个next事件
let ofObservable = Observable.of(1, 2, 3) // 通过可变参数来生成多个next事件

// 等价于
let observable = Observable<Int>.create { observer in
    observer.onNext(1)
    observer.onNext(2)
    observer.onNext(3)
    observer.onCompleted()
    return Disposables.create()
}
// 重复N次Observable:给定一个重复次数N,该Observable就会重复发出同一个next事件N次 + 一个completed事件
let repeatElementObservable = Observable.repeatElement(1)
repeatElementObservable.take(5)
// 定时器Observable:完全就是一个定时器,必须泛整型。延迟多长时间、每隔多长时间、在哪个线程,该Observable会像个定时器一样不断发出累加的整数
//
// 如果想取消定时器,其实不用取消定时器Observable,你只需要把监听行为给释放掉就可以了,这样的话定时器Observable还是存在的,它并不会发出completed事件或error事件
let timerObservable = Observable<Int>.timer(.seconds(3), period: .seconds(1), scheduler: MainScheduler.instance)

3、Observer要做的处理

这个没什么好说的,通常就是一个闭包。

4、Observer

Observer有两种类型:AnyObserver(随便什么东西都能充当AnyObserver这种类型的观察者)和Binder(一般都是一些UI控件的属性在充当Binder这种类型的观察者),但它们都是一个泛型结构体:

// 注意:类型为ObserverType
public struct AnyObserver<Element> : ObserverType {
    ...
}

// 注意:类型为ObserverType
public struct Binder<Element>: ObserverType {
    ...
}

Observer就是观察者。Observer的作用就是监听Observable(至于怎么监听,会在下一小节说),并在收到Observable发出的事件时做出相应的处理,需要注意的是Observer想要监听的数据的数据类型就是泛型的这个Element,如果它跟Observable和Event那里的泛型Element不一样,我们就得先通过map等函数把数据类型给搞一样。

  • 创建一个AnyObserver及AnyObserver做相应的处理
// 1、怎么创建一个anyObeserver?
//
// 调用AnyObserver的init方法就可以创建一个anyObserver,假设这个anyObserver想要监听的数据的数据类型为Int
//
// init方法的入参是一个闭包,这个闭包的入参是一个event——这个event就是将来被观察的Observable所发出的事件,这个闭包的执行体就是AnyObserver要做的处理
let anyObserver = AnyObserver<Int>.init { event in
    // 2、anyObserver怎么做出相应的处理?
    // 在这个闭包的执行体里做相应的处理即可
    switch event {
    case .next:
        print("next:\(event.element)")
    case .error:
        print("error:\(event.error)")
    case .completed:
        print("completed")
    }
}
  • 创建一个Binder及Binder做相应的处理
// 1、怎么创建一个Binder?
//
// 调用Binder的init方法就可以创建一个binder,假设这个binder想要监听的数据的数据类型为String(假设我们想监听字符串数据,把监听到的字符串数据设置到一个label上显示)
//
// init方法的第一个入参是指你要把监听到的数据设置给谁来使用,通常是一个UI控件
// init方法的第二个入参是一个闭包,这个闭包的第一个入参就是前面的那个UI控件,第二个入参就是监听到的字符串数据,这个闭包的执行体就是binder要做的处理
let binder = Binder<String>.init(label) { label, element in
    // 2、binder怎么做出相应的处理?
    // 在这个闭包的执行体里做相应的处理即可
    label.text = element
}


三、Observer监听Observable,以及监听行为的释放


1、Observer监听Observable

现在我们已经成功拥有了四大核心角色,被观察者 + 被观察者要发出的事件、观察者 + 观察者要做的处理,那这两大阵营怎么联系起来呢,也就是说观察者怎么才能收到被观察者发出的事件、进而触发相应的处理呢?很简单,就是Observer监听Observable。

  • AnyObserver监听Observable
let timerObservable = Observable<Int>.timer(.seconds(3), period: .seconds(1), scheduler: MainScheduler.instance)
let anyObserver = AnyObserver<Int>.init { event in
    switch event {
    case .next:
        print("next:\(event.element)")  // 会打印:0、1、2、3、...
    case .error:
        print("error:\(event.error)")
    case .completed:
        print("completed")
    }
}

// anyObserver监听timerObservable,就这么简单
timerObservable.subscribe(anyObserver)
  • Binder监听Observable
// 界面上搞一个label
@IBOutlet weak var label: UILabel!

let timerObservable = Observable<Int>.timer(.seconds(3), period: .seconds(1), scheduler: MainScheduler.instance)
let binder = Binder<String>.init(label) { label, element in
    label.text = element
}

// binder监听timerObservable
// 但是timerObservable发出的事件挂的数据是Int类型
// 而binder监听的确实String类型
// 所以得把timerObservable发出的事件挂的Int数据通过map函数转换成String数据
// 就这么简单
timerObservable
    .map { element in
        return "数字是:\(element)"
    }
    .subscribe(binder)

// Binder还有一个专门的bindTo方法用来监听Observable,不过跟subscribe方法是等价的
timerObservable
    .map { element in
        return "数字是:\(element)"
    }
    .bind(to: binder)
  • 当然除了AnyObserver和Binder这种标标准准的Observer可以监听Observable之外,我们还可以直接用一个闭包来监听Observable,这种情况下这个闭包其实也是一个广义上的观察者,而且这个观察者是把原来标标准准的观察者和观察者要做的处理两个角色融为一体了
let timerObservable = Observable<Int>.timer(.seconds(3), period: .seconds(1), scheduler: MainScheduler.instance)

// 方式1
timerObservable
    .subscribe { event in
        switch event {
        case .next:
            print("next:\(event.element)")  // 会打印:0、1、2、3、...
        case .error:
            print("error:\(event.error)")
        case .completed:
            print("completed")
        }
    }

// 方式2
timerObservable
    .subscribe(onNext: { element in
        print("next:\(element)")  // 会打印:0、1、2、3、...
    }, onError: { error in
        print("error:\(error)")
    }, onCompleted: {
        print("completed")
    })

因此将来我们看到subscribe方法或bindTo方法前面的东西肯定就是个Observable,后面的东西肯定就是个广义的Observer——标标准准的Observer + 闭包。

2、监听行为的释放

其实每当Observer监听Observable时就产生了一个监听行为——即subscribe方法或bindTo方法会返回一个Disposable类型的实例,一定要记得在适当的时机释放这个监听行为,以免内存泄漏,注意当这个监听行为被释放后,仅仅是切断了两大阵营之间的联系,Observable和Observer这些对象本身是没被销毁的。释放的方式有三种:

  • 立即释放监听行为(实际开发中使用较少)
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let timerObservable = Observable<Int>.timer(.seconds(3), period: .seconds(1), scheduler: MainScheduler.instance)
        let anyObserver = AnyObserver<Int>.init { event in
            switch event {
            case .next:
                print("next:\(event.element)")
            case .error:
                print("error:\(event.error)")
            case .completed:
                print("completed")
            }
        }

        let disposable = timerObservable.subscribe(anyObserver)
        // 立即释放监听行为——即接收完释放之前的一波数据后立即释放,类似于release
        disposable.dispose()
    }
}
  • 自动释放监听行为(实际开发中使用较多)
class ViewController: UIViewController {
    // 类似于autoreleasepool
    //
    // 它会把所有的监听行为都放进去,等到它即将销毁时再对它里面所有的监听行为都调用一次dispose方法
    // 现在bag属性的生命周期是跟当前viewController一样的
    let bag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let timerObservable = Observable<Int>.timer(.seconds(3), period: .seconds(1), scheduler: MainScheduler.instance)
        let anyObserver = AnyObserver<Int>.init { event in
            switch event {
            case .next:
                print("next:\(event.element)")
            case .error:
                print("error:\(event.error)")
            case .completed:
                print("completed")
            }
        }
        
        timerObservable.subscribe(anyObserver).disposed(by: bag)
    }
}
  • 手动释放监听行为
class ViewController: UIViewController {
    // 定义一个属性
    var disposable: Disposable?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let timerObservable = Observable<Int>.timer(.seconds(3), period: .seconds(1), scheduler: MainScheduler.instance)
        let anyObserver = AnyObserver<Int>.init { event in
            switch event {
            case .next:
                print("next:\(event.element)")
            case .error:
                print("error:\(event.error)")
            case .completed:
                print("completed")
            }
        }
        
        // 把监听行为记录下来
        disposable = timerObservable.subscribe(anyObserver)
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        // 在合适的时机手动释放监听行为
        disposable?.dispose()
    }
}


四、Subjects


Subjects同时充当了Observable和Observer的角色,也就是说它既是一个被观察者、也是一个观察者,它既能发出事件、也能做相应的处理。常用的Subjects有四种:

  • PublishSubject
class ViewController: UIViewController {
    let bag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let publishSubject = PublishSubject<Int>.init()
        
        // 这里publishSubject在充当Observer的角色,帮Observable发出事件
        // 还记得之前创建Observable时,Observable是怎么发出事件的吗?就是给Observer发送onNext消息发出的,onNext本来就是Observer的方法,所以这里就体现了publishSubject在充当Observer的角色
        publishSubject.onNext(1)
        publishSubject.onNext(2)
        publishSubject.onNext(3)
        
        // 这里publishSubject在充当Observable的角色,只会向观察者发送观察者监听它之后的事件,不会向观察者发送观察者监听它之前的事件
        publishSubject.subscribe { event in
            print(event) // 4、5、6
        }.disposed(by: bag)
        
        
        publishSubject.onNext(4)
        publishSubject.onNext(5)
        publishSubject.onNext(6)
    }
}
  • BehaviorSubject
class ViewController: UIViewController {
    let bag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()

        let behaviorSubject = BehaviorSubject<Int>(value: 1)

        // 这里behaviorSubject在充当Observer的角色,帮Observable发出事件
        behaviorSubject.onNext(2)
        behaviorSubject.onNext(3)

        // 这里behaviorSubject在充当Observable的角色,会向观察者发送观察者监听它之前的最后一个事件 + 观察者监听它之后的事件
        behaviorSubject.subscribe { event in
            print(event) // 3、4、5、6
        }.disposed(by: bag)

        behaviorSubject.onNext(4)
        behaviorSubject.onNext(5)
        behaviorSubject.onNext(6)
    }
}
  • ReplaySubject
class ViewController: UIViewController {
    let bag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let replaySubject = ReplaySubject<Int>.create(bufferSize: 2) // 假设要缓存两个事件
        
        // 这里replaySubject在充当Observer的角色,帮Observable发出事件
        replaySubject.onNext(1)
        replaySubject.onNext(2)
        replaySubject.onNext(3)
        
        // 这里replaySubject在充当Observable的角色,会向观察者发送观察者监听它之前缓存的N个事件 + 观察者监听它之后的事件
        replaySubject.subscribe { event in
            print(event) // 2、3、4、5、6。缓存的是最后两个事件,因为是序列,所以会按顺序把事件发出来
        }.disposed(by: bag)
        
        replaySubject.onNext(4)
        replaySubject.onNext(5)
        replaySubject.onNext(6)
    }
}
  • BehaviorRelay(Variable已经被废弃了,用BehaviorRelay代替,它俩其实都是对BehaviorSubject的封装,功能一样)
class ViewController: UIViewController {
    let bag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let behaviorRelay = BehaviorRelay<Int>.init(value: 1)
        
        // 这里behaviorRelay在充当Observer的角色,帮Observable发出事件
        behaviorRelay.accept(2)
        behaviorRelay.accept(3)
        
        // 这里behaviorRelay在充当Observable的角色,会向观察者发送观察者监听它之前的最后一个事件 + 观察者监听它之后的事件
        behaviorRelay.subscribe { event in
            print(event) // 3、4、5、6
        }.disposed(by: bag)
        
        behaviorRelay.accept(4)
        behaviorRelay.accept(5)
        behaviorRelay.accept(6)
    }
}

参考
1、RxSwift中文文档
2、RxSwift大全

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容