RxSwift 使用方式

背景

最近项目业务,所有模块已经支持Swift混编开发,正在逐步使用Swift 方式进行开发新业务,以及逐步替换老业务方式进行发展,所以使用一些较为成熟的Swift 的三方库,成为必要性,经过调研发现RxSwift 在使用的情况上,较为成熟,且方便实用;以下介绍一些RxSwift 使用的一些方式和大家一起学习讨论

Swift为值类型,在传值与方法回调上有影响,RxSwift一定程度上弥补Swift的灵活性

  • RxSwift使得代码复用性较强,减少代码量
  • RxSwift因为声明都是不可变更,增加代码可读性
  • RxSwift使得更易于理解业务代码,抽象异步编程,统一代码风格
  • RxSwift使得代码更易于编写集成单元测试,增加代码稳定性

一:简单的使用方式

1.1 信号创建

1.1.1创建一个普通信号
let disposeB = DisposeBag()//通过指定的方法实现来自定义一个被观察的序列。//订阅创建let myOb = Observable<Any>.create { (observ) -> Disposable inobserv.onNext("alan")observ.onCompleted()return Disposables.create()}//订阅事件myOb.subscribe { (even) inprint("subscribe" + "\(even)")}.disposed(by: disposeB)//销毁1.1.2 创建信号的其他方式class ViewController: UIViewController {let disposeB = DisposeBag()override func viewDidLoad() {super.viewDidLoad()// Do any additional setup after loading the view, typically from a nib.//通过指定的方法实现来自定义一个被观察的序列。//订阅创建let myOb = Observable<Any>.create { (observ) -> Disposable inobserv.onNext("alan")zipSigal observ.onCompleted()return Disposables.create()}//订阅事件myOb.subscribe { (even) inprint("subscribe" + "\(even)")}.disposed(by: disposeB)//销毁//各种观察者序列产生方式//该方法通过传入一个默认值来初始化。Observable.just("just").subscribe { (event) inprint(event)}.disposed(by: disposeB)//该方法可以接受可变数量的参数(必需要是同类型的)Observable.of("o","f","of").subscribe { (event) inprint(event)}.disposed(by: disposeB)//该方法需要一个数组参数。Observable.from(["f","r","o","m"]).subscribe { (event) inprint("from" + "\(event)")}.disposed(by: disposeB)//该方法创建一个永远不会发出 Event(也不会终止)的 Observable 序列。Observable<Int>.never().subscribe { (event) inprint(event)}.disposed(by: disposeB)// // 该方法创建一个空内容的 Observable 序列。 //会打印completeObservable<Int>.empty().subscribe { (event) inprint("empty" ,event)}.disposed(by: disposeB)//该方法创建一个不做任何操作,而是直接发送一个错误的 Observable 序列。let myError = MyError.Aprint(myError.errorType)Observable<Int>.error(myError).subscribe { (event) inprint(event.error)}.disposed(by: disposeB)//该方法通过指定起始和结束数值,创建一个以这个范围内所有值作为初始值的Observable序列。Observable.range(start: 1, count: 6).subscribe { (event) inprint(event)}.disposed(by: disposeB)//该方法创建一个可以无限发出给定元素的 Event的 Observable 序列(永不终止)。慎重使用// Observable.repeatElement("SPAlan").subscribe { (event) in// print(event)// }.disposed(by: disposeB)//该方法创建一个只有当提供的所有的判断条件都为 true 的时候,才会给出动作的 Observable 序列。//第一个参数:初始化的数值 第二个 条件 第三也是一个条件 0 + 2 <= 10 依次循环下去,iterate:重复执行 ,执行结果为 0,2,4,6,8,10Observable.generate(initialState: 0, condition: {$0<=10}, iterate: {$0+2}).subscribe { (event) inprint(event)}.disposed(by: disposeB)//上面和下面的效果一样Observable.of(0,2,4,6,8,10).subscribe { (event) inprint(event)}.disposed(by: disposeB)//该个方法相当于是创建一个 Observable 工厂,通过传入一个 block 来执行延迟 Observable序列创建的行为,而这个 block 里就是真正的实例化序列对象的地方。var isOdd = truelet factory: Observable<Int> = Observable.deferred { () -> Observable<Int> inisOdd = !isOddif isOdd{return Observable.of(0,2,4,6,8)}else{return Observable.of(1,3,5,7,9)}}//这里会调用上面的工厂factory.subscribe { (event) inprint("\(isOdd)",event)}.disposed(by: disposeB)//这里会再次调用工厂factory.subscribe { (event) inprint("\(isOdd)",event)}.disposed(by: disposeB)//这个方法创建的 Observable 序列每隔一段设定的时间,会发出一个索引数的元素。而且它会一直发送下去。// Observable<Int>.interval(DispatchTimeInterval.seconds(1), scheduler: MainScheduler.instance).subscribe { (event) in// print(event)// }.disposed(by: disposeB)//这个方法有两种用法,一种是创建的 Observable序列在经过设定的一段时间后,产生唯一的一个元素。,这个只调用一次Observable<Int>.timer(DispatchTimeInterval.seconds(1), scheduler: MainScheduler.instance).subscribe{(event) inprint("123",event)}.disposed(by: disposeB)//另一种是创建的 Observable 序列在经过设定的一段时间后,每隔一段时间产生一个元素。:经过5秒后每个1秒创建一个元素// Observable<Int>.timer(DispatchTimeInterval.seconds(5), period: DispatchTimeInterval.seconds(1), scheduler: MainScheduler.instance).subscribe { (event) in// print(event)// }.disposed(by: disposeB)}enum MyError:Error {case Acase Bvar errorType:String {switch self {case .A:return "i am error A"case .B:return "BBBB"}}}override func didReceiveMemoryWarning() {super.didReceiveMemoryWarning()// Dispose of any resources that can be recreated.}}

1.2 iOS中的一些用法

1.2.1:button点击事件
//MARK: - RxSwift应用-button响应func setupButton() {// 业务逻辑 和 功能逻辑// 设计self.button.rx.tap.subscribe(onNext: { () inprint("按钮点击了")}).disposed(by: disposeBag)}
1.2.2:textfiled文本响应
//MARK: - RxSwift应用-textfiledfunc setupTextFiled() {self.textFiled.rx.text.orEmpty.subscribe(onNext: { (text) inprint(text)}).disposed(by: disposeBag)// 简单简单 更简单-RxSwift 面向开发者self.textFiled.rx.text.bind(to: self.button.rx.title()).disposed(by: disposeBag)}
1.2.3:scrollView效果
//MARK: - RxSwift应用-scrollViewfunc setupScrollerView() {scrollView.contentSize = CGSize(width: 300, height: 1000)scrollView.rx.contentOffset.subscribe(onNext: { [weak self](content) inprint(content);print(self?.scrollView)}).disposed(by: disposeBag)}
1.2.4:KVO
//MARK: - RxSwift应用-KVOfunc setupKVO() {// 系统KVO 还是比较麻烦的// person.addObserver(self, forKeyPath: "name", options: .new, context: nil)person.rx.observeWeakly(String.self, "name").subscribe(onNext: { (change) inprint(change ?? "helloword")}).disposed(by: disposeBag)}
1.2.5:通知
//MARK: - 通知func setupNotification(){NotificationCenter.default.rx.notification(UIResponder.keyboardWillShowNotification).subscribe { (event) inprint(event)}.disposed(by: disposeBag)}
1.2.6:手势
//MARK: - 手势func setupGestureRecognizer(){let tap = UITapGestureRecognizer()self.label.addGestureRecognizer(tap)self.label.isUserInteractionEnabled = truetap.rx.event.subscribe(onNext: { (tap) inprint(tap.view)}).disposed(by: disposeBag)}
1.2.7:网络请求
//MARK: - RxSwift应用-网络请求func setupNextwork() {let url = URL(string: "https://www.baidu.com")// 传统方式URLSession.shared.dataTask(with: url!) { (data, response, error) inprint("dataTask.response->" + "\(response)")print("dataTask.response->" + "\(data)")}.resume()//RxSwift 方式URLSession.shared.rx.response(request: URLRequest(url: url!)).subscribe(onNext: { (response, data) inprint("rx.response.response ==== \(response)")print("rx.response.data ===== \(data)")}, onError: { (error) inprint("error ===== \(error)")}).disposed(by: disposeBag)}
1.2.8:timer定时器
func setupTimer() {timer = Observable<Int>.interval(DispatchTimeInterval.seconds(1), scheduler: MainScheduler.instance)timer.subscribe(onNext: { (num) inprint("hello word \(num)")}).disposed(by: disposeBag)}

二:高阶用法

2.1:组合操作符

2.1.1:startWith
print("*****startWith*****")Observable.of("1", "2", "3", "4").startWith("A").startWith("B").startWith("C", "a", "b").subscribe(onNext: { print($0) }).disposed(by: disposeBag)//效果: CabBA1234
2.1.2:merge
print("*****merge*****")let subject1 = PublishSubject<String>()let subject2 = PublishSubject<String>()// merge subject1和subject2Observable.of(subject1, subject2).merge().subscribe(onNext: { print($0) }).disposed(by: disposeBag)subject1.onNext("C")subject1.onNext("o")subject2.onNext("o")subject2.onNext("o")subject1.onNext("c")subject2.onNext("i")
2.1.3:zip
  • 将多达8个原可观测序列组合成一个新的可观测序列,并将从组合的可观测序列中发射出对应索引处每个原可观测序列的元素
print("*****zip*****")let stringSubject = PublishSubject<String>()let intSubject = PublishSubject<Int>()Observable.zip(stringSubject, intSubject) { stringElement, intElement in"\(stringElement) \(intElement)"}.subscribe(onNext: { print($0) }).disposed(by: disposeBag)stringSubject.onNext("C")stringSubject.onNext("o") // 到这里存储了 C o 但是不会响应除非;另一个响应intSubject.onNext(1) // 勾出一个intSubject.onNext(2) // 勾出另一个stringSubject.onNext("i") // 存一个intSubject.onNext(3) // 勾出一个// 说白了: 只有两个序列同时有值的时候才会响应,否则存值
2.1.4:combineLatest
  • 将原可观测序列组合成一个新的观测序列,并将开始发出联合观测序列的每个元素最新元素可观测序列一旦所有排放原序列至少有一个元素,并且当原可观测序列发出的任何一个新元素
print("*****combineLatest*****")let stringSub = PublishSubject<String>()let intSub = PublishSubject<Int>()Observable.combineLatest(stringSub, intSub) { strElement, intElement in"\(strElement) \(intElement)"}.subscribe(onNext: { print($0) }).disposed(by: disposeBag)stringSub.onNext("S") // 存一个 SstringSub.onNext("P") // 存了一个覆盖 - 和zip不一样intSub.onNext(1) // 发现strOB也有G 响应 G 1intSub.onNext(2) // 覆盖1 -> 2 发现strOB 有值G 响应 G 2stringSub.onNext("alan") // 覆盖G -> alan 发现intOB 有值2 响应 alan 2// combineLatest 比较zip 会覆盖// 应用非常频繁: 比如账户和密码同时满足->才能登陆. 不关系账户密码怎么变化的只要查看最后有值就可以 loginEnable输出结果:*****combineLatest*****P 1P 2alan 2

2.1.5:switchLatest
  • 将可观察序列发出的元素转换为可观察序列,并从最近的内部可观察序列发出元素
print("*****switchLatest*****")let switchLatestSub1 = BehaviorSubject(value: "L")let switchLatestSub2 = BehaviorSubject(value: "1")let switchLatestSub = BehaviorSubject(value: switchLatestSub1)// 选择了 switchLatestSub1 就不会监听 switchLatestSub2switchLatestSub.asObservable().switchLatest().subscribe(onNext: { print($0) }).disposed(by: disposeBag)switchLatestSub1.onNext("G")switchLatestSub1.onNext("_")switchLatestSub2.onNext("2")switchLatestSub2.onNext("3") // 2-3都会不会监听,但是默认保存由 2覆盖1 3覆盖2switchLatestSub.onNext(switchLatestSub2) // 切换到 switchLatestSub2switchLatestSub1.onNext("*")switchLatestSub1.onNext("alan") // 原理同上面 下面如果再次切换到 switchLatestSub1会打印出 alanswitchLatestSub2.onNext("4")输出结果为:*****switchLatest*****LG_34

2.2:映射操作符

2.2.1:map
  • 转换闭包应用于可观察序列发出的元素,并返回转换后的元素的新可观察序列。
print("*****map*****")let ob = Observable.of(1,2,3,4)ob.map { (number) -> Int inreturn number+2}.subscribe{print("\($0)")}.disposed(by: disposeBag)
2.2.2:flatMap and flatMapLatest
  • 将可观测序列发射的元素转换为可观测序列,并将两个可观测序列的发射合并为一个可观测序列。
  • 这也很有用,例如,当你有一个可观察的序列,它本身发出可观察的序列,你想能够对任何一个可观察序列的新发射做出反应(序列中序列:比如网络序列中还有模型序列)
  • flatMap和flatMapLatest的区别是,flatMapLatest只会从最近的内部可观测序列发射元素
struct SPPlayer {var score: BehaviorRelay<Int>}func flatMapLatest(){print("*****flatMapLatest*****")var boy = SPPlayer(score: BehaviorRelay(value: 100))var girl = SPPlayer(score: BehaviorRelay(value: 90))var player = BehaviorSubject(value: boy)player.asObservable().flatMapLatest{$0.score.asObservable() } // 本身score就是序列 模型就是序列中的序列.subscribe(onNext: { print($0) }).disposed(by: disposeBag)boy.score.accept(75)player.onNext(girl)boy.score.accept(50)boy.score.accept(40)// 如果切换到 flatMapLatest 就不会打印girl.score.accept(10)girl.score.accept(0)// 输出结果为:100,75,90,10,0}func flatMap(){print("*****flatMap*****")var boy = SPPlayer(score: BehaviorRelay(value: 100))var girl = SPPlayer(score: BehaviorRelay(value: 90))var player = BehaviorSubject(value: boy)player.asObservable().flatMap {$0.score.asObservable() } // 本身score就是序列 模型就是序列中的序列.subscribe(onNext: { print($0) }).disposed(by: disposeBag)boy.score.accept(75)player.onNext(girl)boy.score.accept(50)boy.score.accept(40)// 如果切换到 flatMapLatest 就不会打印girl.score.accept(10)girl.score.accept(0)// 输出结果为:100,75,90,50,40,10,0}
2.2.3:scan
  • 从初始就带有一个默认值开始,然后对可观察序列发出的每个元素应用累加器闭包,并以单个元素可观察序列的形式返回每个中间结果
print("*****scan*****")Observable.of(10, 100, 1000).scan(2) { aggregateValue, newValue inaggregateValue + newValue // 10 + 2 , 100 + 10 + 2 , 1000 + 100 + 2}.subscribe(onNext: { print($0) }).disposed(by: disposeBag)// 这里主要强调序列值之间的关系

2.3:过滤条件操作符

2.3.1:filter
  • 仅从满足指定条件的可观察序列中发出那些元素
print("*****filter*****")Observable.of(1,2,3,4,5,6,7,8,9,0).filter { $0 % 2 == 0 }.subscribe(onNext: { print($0) }).disposed(by: disposeBag)

2.3.2:distinctUntilChanged
  • 抑制可观察序列发出的顺序重复元素
print("*****distinctUntilChanged*****")Observable.of("1", "2", "2", "2", "3", "3", "4").distinctUntilChanged().subscribe(onNext: { print($0) }).disposed(by: disposeBag)
2.3.3:elementAt
  • 仅在可观察序列发出的所有元素的指定索引处发出元素
 
print("*****elementAt*****")Observable.of("A", "l", "a", "n", "z").element(at: 3).subscribe(onNext: { print($0) }).disposed(by: disposeBag)

2.3.4:single
  • 只发出可观察序列发出的第一个元素(或满足条件的第一个元素)。如果可观察序列发出多个元素,将抛出一个错误。
 
print("*****single*****")Observable.of("alan", "zhi").single().subscribe(onNext: { print($0) }).disposed(by: disposeBag)Observable.of("zhai", "zhuang").single { $0 == "zhuang" }.subscribe (onNext:{ print($0,"---single---") }).disposed(by: disposeBag)/**输出结果:alanUnhandled error happened: Sequence contains more than one element.zhuang ---single---**/

2.3.5:take
  • 只从一个可观察序列的开始发出指定数量的元素。 上面signal只有一个序列 在实际开发会受到局限 这里引出 take 想几个就几个
print("*****take*****")Observable.of("alan", "zhuang","xing", "zhai").take(2).subscribe(onNext: { print($0) }).disposed(by: disposeBag)

2.3.6:takeLast
  • 仅从可观察序列的末尾发出指定数量的元素
print("*****takeLast*****")Observable.of("alan", "zhuang","xing", "zhai").takeLast(3).subscribe(onNext: { print($0) }).disposed(by: disposeBag)//输出结果:zhuang xing zhai2.3.7:takeWhile
只要指定条件的值为true,就从可观察序列的开始发出元素print("*****takeWhile*****")Observable.of(1, 2, 3, 4, 5, 6).take(while: { int inint < 3}).subscribe(onNext: { print($0) }).disposed(by: disposeBag)// 输出结果 1,2

2.3.8:takeUntil
  • 从原序列可观察序列发出元素,直到参考可观察序列发出元素
  • 这个要重点,应用非常频繁 比如当前页面销毁了,就不能获取值了(cell重用运用)
print("*****takeUntil*****")let sourceSequence = PublishSubject<String>()let referenceSequence = PublishSubject<String>()sourceSequence.take(until: referenceSequence).subscribe (onNext:{ print($0) }).disposed(by: disposeBag)sourceSequence.onNext("alan")sourceSequence.onNext("zhai")sourceSequence.onNext("xing")referenceSequence.onNext("zhuang") // 条件一出来,下面就走不了sourceSequence.onNext("qiang")sourceSequence.onNext("yanzi")sourceSequence.onNext("ting")输出结果为:*****takeUntil*****alanzhaixing

2.3.9:skip
  • 从原序列可观察序列发出元素,直到参考可观察序列发出元素
  • 这个要重点,应用非常频繁 不用解释 textfiled 都会有默认序列产生
print("*****skip*****")Observable.of(1, 2, 3, 4, 5, 6).skip(2).subscribe(onNext: { print($0) }).disposed(by: disposeBag)print("*****skipWhile*****")Observable.of(1, 2, 3, 4, 5, 6).skip(while :{int inint < 3}).subscribe(onNext: { print($0) }).disposed(by: disposeBag)

2.3.10:skipUntil
  • 抑制从原序列可观察序列发出元素,直到参考可观察序列发出元素
 
print("*****skipUntil*****")let sourceSeq = PublishSubject<String>()let referenceSeq = PublishSubject<String>()sourceSeq.skip(until: referenceSeq).subscribe(onNext: { print($0) }).disposed(by: disposeBag)// 没有条件命令 下面走不了sourceSeq.onNext("alan")sourceSeq.onNext("zhai")sourceSeq.onNext("qiang")referenceSeq.onNext("zhuang") // 条件一出来,下面就可以走了sourceSeq.onNext("lilei")sourceSeq.onNext("yanzi")sourceSeq.onNext("meimei")输出结果为:*****skipUntil*****lileiyanzimeimei

2.4:集合控制操作符

2.4.1:toArray
  • 将一个可观察序列转换为一个数组,将该数组作为一个新的单元素可观察序列发出,然后终止
print("*****toArray*****")Observable.range(start: 1, count: 10).toArray().subscribe(onSuccess: { print($0) }).disposed(by: disposeBag)// 输出结果:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

2.4.2:reduce
  • 从一个设置的初始化值开始,然后对一个可观察序列发出的所有元素应用累加器闭包,并以单个元素可观察序列的形式返回聚合结果 - 类似scan
 
print("*****reduce*****")Observable.of(10, 100, 1000).reduce(1, accumulator: +) // 1 + 10 + 100 + 1000 = 1111.subscribe(onNext: { print($0) }).disposed(by: disposeBag)// 输出结果:1111
2.4.3:concat
  • 以顺序方式连接来自一个可观察序列的内部可观察序列的元素,在从下一个序列发出元素之前,等待每个序列成功终止
print("*****concat*****")let subject1 = BehaviorSubject(value: "zhai")let subject2 = BehaviorSubject(value: "1")let subjectsSubject = BehaviorSubject(value: subject1)subjectsSubject.asObservable().concat().subscribe (onNext:{ print($0) }).disposed(by: disposeBag)subject1.onNext("alan")subject1.onNext("xing")subjectsSubject.onNext(subject2)subject2.onNext("打印不出来")subject2.onNext("2")subject1.onCompleted() // 必须要等subject1 完成了才能订阅到! 用来控制顺序 网络数据的异步subject2.onNext("3")

2.5:从可观察对象的错误通知中恢复的操作符

2.5.1:catchAndReturn
  • 从错误事件中恢复,方法是返回一个可观察到的序列,该序列发出单个元素,然后终止
enum SPError:Error {case Acase Bvar errorType:String {switch self {case .A:return "i am error A"case .B:return "BBBB"}}}let spError = SPError.Aprint("*****catchAndReturn*****")let sequenceThatFails = PublishSubject<String>()sequenceThatFails.catchAndReturn("alan").subscribe (onNext: { print($0) }).disposed(by: disposeBag)sequenceThatFails.onNext("qiang")sequenceThatFails.onNext("lilei") // 正常序列发送成功的//发送失败的序列,一旦订阅到位 返回我们之前设定的错误的预案sequenceThatFails.onError(self.spError)

5.2:catch
  • 通过切换到提供的恢复可观察序列,从错误事件中恢复
print("*****catch*****")let sequenceThatFails = PublishSubject<String>()let recoverySequence = PublishSubject<String>()sequenceThatFails.catch {print("Error:", $0)return recoverySequence}.subscribe { print($0) }.disposed(by: disposeBag)sequenceThatFails.onNext("😬")sequenceThatFails.onNext("😨")sequenceThatFails.onNext("😡")sequenceThatFails.onNext("🔴")sequenceThatFails.onError(self.spError)recoverySequence.onNext("😊")5.3:retry
通过无限地重新订阅可观察序列来恢复重复的错误事件print("*****retry*****")var count = 1 // 外界变量控制流程let sequenceRetryErrors = Observable<String>.create { observer inobserver.onNext("alan")observer.onNext("xing")observer.onNext("zhai")if count == 1 {// 流程进来之后就会过度-这里的条件可以作为出口,失败的次数observer.onError(self.spError) // 接收到了错误序列,重试序列发生print("错误序列来了")count += 1}observer.onNext("liLie")observer.onNext("yanzi")observer.onNext("ting")observer.onCompleted()return Disposables.create()}sequenceRetryErrors.retry().subscribe(onNext: { print($0) }).disposed(by: disposeBag)/***输出结果:*****retry*****alanxingzhai错误序列来了alanxingzhailiLieyanziting*/

5.4:retry(_:):
  • 通过重新订阅可观察到的序列,重复地从错误事件中恢复,直到重试次数达到max未遂计数
 
print("*****retry(_:)*****")var count = 1let sequenceThatErrors = Observable<String>.create { observer inobserver.onNext("alan")observer.onNext("xing")observer.onNext("qiangqiang")if count < 5 { // 这里设置的错误出口是没有太多意义的额,因为我们设置重试次数observer.onError(self.spError)print("错误序列来了")count += 1}observer.onNext("liLei")observer.onNext("yanzi")observer.onNext("ting")observer.onCompleted()return Disposables.create()}sequenceThatErrors.retry(3).subscribe(onNext: { print($0) }).disposed(by: disposeBag)

2.6:Rx流程操作符。

2.6.1:debug
  • 打印所有订阅、事件和处理。
print("*****debug*****")var count = 1let sequenceThatErrors = Observable<String>.create { observer inobserver.onNext("alan")observer.onNext("zhai")observer.onNext("qiang")if count < 5 {observer.onError(self.spError)print("错误序列来了")count += 1}observer.onNext("liLei")observer.onNext("yanzi")observer.onNext("zhuang")observer.onCompleted()return Disposables.create()}sequenceThatErrors.retry(3).debug().subscribe(onNext: { print($0) }).disposed(by: disposeBag)

2.7:链接操作符

2.7.1:multicast
  • 将原可观察序列转换为可连接序列,并通过指定的主题广播发射。
func testMulticastConnectOperators(){print("*****multicast*****")let netOB = Observable<Any>.create { (observer) -> Disposable insleep(2)// 模拟网络延迟print("我开始请求网络了")observer.onNext("请求到的网络数据")observer.onNext("请求到的本地")observer.onCompleted()return Disposables.create {print("销毁回调了")}}.publish()netOB.subscribe(onNext: { (anything) inprint("订阅1:",anything)}).disposed(by: disposeBag)// 我们有时候不止一次网络订阅,因为有时候我们的数据可能用在不同的地方// 所以在订阅一次 会出现什么问题?netOB.subscribe(onNext: { (anything) inprint("订阅2:",anything)}).disposed(by: disposeBag)/**输出结果为:*****multicast*****我开始请求网络了订阅1: 请求到的网络数据订阅2: 请求到的网络数据订阅1: 请求到的本地订阅2: 请求到的本地销毁回调了**/_ = netOB.connect()}

2.7.2:replay
  • 将原可观察序列转换为可连接的序列,并将向每个新订阅服务器重放以前排放的缓冲大小
  • 首先拥有和publish一样的能力,共享 Observable sequence, 其次使用replay还需要我们传入一个参数(buffer size)来缓存已发送的事件,当有新的订阅者订阅了,会把缓存的事件发送给新的订阅者
print("*****replay*****")let intSequence = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance).replay(5)_ = intSequence.subscribe(onNext: { print(Date(),"订阅 1:, Event: \($0)") })DispatchQueue.main.asyncAfter(deadline: .now() + 2) {_ = intSequence.connect()}DispatchQueue.main.asyncAfter(deadline: .now() + 4) {_ = intSequence.subscribe(onNext: { print(Date(),"订阅 2:, Event: \($0)") })}DispatchQueue.main.asyncAfter(deadline: .now() + 8) {_ = intSequence.subscribe(onNext: { print(Date(),"订阅 3:, Event: \($0)") })}DispatchQueue.main.asyncAfter(deadline: .now() + 10) {self.disposeBag = DisposeBag()}

三:示例

3.1:简单实用RxSwift 做一个计时器功能,控件绑定数据基础用法

计时器源码

文章Demo源码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/4982.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

lvs使用

1.前言 LVS&#xff08;Linux Virtual Server&#xff09;是一个基于 Linux 内核的负载均衡器&#xff0c;用于分发网络流量和将请求转发给后端服务器。LVS 提供了多种负载均衡算法和转发模式&#xff0c;以满足不同场景和需求的负载均衡需求&#xff0c;在LVS中定义虚拟服务的…

制作Visual Studio离线安装包

vs2015之后官网就不提供离线安装包了&#xff0c;使用离线安装包就需要自己手动制作一个&#xff1b; 以vs2019为例&#xff1a; 先去官网下载在线安装器 官网下载地址&#xff1a;Visual Studio 较旧的下载 - 2019、2017、2015 和以前的版本 (microsoft.com) 展开2019的标签…

【C语言】深剖数据在内存中的存储

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在回炉重造C语言&#xff08;2023暑假&#xff09; ✈️专栏&#xff1a;【C语言航路】 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你…

初识react

初识react 第一步就给我出个问题版本太低 https://www.cnblogs.com/gslgb/p/16585233.html https://blog.csdn.net/xiangshiyufengzhong/article/details/124193898 第二个问题 便利生成dom 需要绑定key 不要总想着加冒号这不是vue 第三个问题 我p标签包裹 MapList组件 MapLis…

Redis相关配置(3)

⭐ 作者简介&#xff1a;码上言 ⭐ 代表教程&#xff1a;Spring Boot vue-element 开发个人博客项目实战教程 ⭐专栏内容&#xff1a;个人博客系统 ⭐我的文档网站&#xff1a;http://xyhwh-nav.cn/ 文章目录 Redis相关配置1、units2、Include3、loadmodule 加载模块4、NET…

创意网页模板免费下载,让你的网站与众不同!

今天给大家带来的网站模板素材&#xff0c;网站类型丰富&#xff0c;包含户外旅行、餐饮、个人网站等等&#xff0c;可以学习和参考其中的布局排版和配色。 ⬇⬇⬇点击获取更多设计资源 https://js.design/community?categorydesign&sourcecsdn&planbbqcsdn772 1、设…

【1++的C++初阶】之vector

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的C初阶】 文章目录 一&#xff0c;什么是vector?二&#xff0c;构造与析构三&#xff0c;vector迭代器的实现四&#xff0c;vector部分重要接口的实现 一&#xff0c;什么是vector? vector…

使用NVIDIA FX Composer验证多纹理合成效果

最近项目上有一个需求&#xff0c;需要将4张带透明通道纹理合成为一张&#xff0c;并且每张纹理指定一个全局透明度。由于纹理过多&#xff0c;合成效果无法保证&#xff0c;为了减少项目的风险&#xff0c;领导希望我先快速验证一下我们讨论的方法是否能完成项目的要求。因此我…

销售易的12年与七个瞬间

导读&#xff1a;企业级没有捷径 12年对一家企业意味着什么&#xff1f; 在消费互联网领域&#xff0c;12年足够长&#xff0c;短短几年内上市的故事过去屡见不鲜。在企业服务的toB领域&#xff0c;产业成熟和企业发展的时间维度被拉长&#xff0c;但故事同样精彩。 2023年7月1…

ylb-接口5产品详情

总览&#xff1a; 1、service处理&#xff08;根据产品id &#xff0c;查询产品信息&#xff09; 在api模块下service包&#xff0c;ProductService接口添加新方法&#xff08;根据产品id &#xff0c;查询产品信息queryById(Integer id)&#xff09;&#xff1a; package …

Python venv 和 virtualenv 虚拟环境的基本使用

1.前言 venv 和 virtualenv 都是搭建虚拟环境的工具&#xff0c;virtualenv 是第三方开源的&#xff0c;而 venv 作为 virtualenv 的一个子集自 Python3.3 开始集成到标准库中&#xff0c;在 virtualenv 的文档中可以看到他们的区别&#xff1a; 没有 app-data 种子方法&#…

Python爬虫——urllib_post请求百度翻译

post请求&#xff1a; post的请求参数&#xff0c;是不会拼接在url后面的&#xff0c;而是需要放在请求对象定制的参数中 post请求的参数需要进行两次编码&#xff0c;第一次urlencode&#xff1a;对字典参数进行Unicode编码转成字符串&#xff0c;第二次encode&#xff1a;将字…

isaac sim添加孔网格

isaac sim仿真和其它仿真实际上一样&#xff0c;对于孔的仿真&#xff0c;是没那么简单的 在此记录一下踩过的坑 1&#xff0c;首先&#xff0c;你需要在soildworks中将你的孔画出来&#xff0c;并导出stl 2&#xff0c;你可以在win10中使用3D画图查看孔的网格&#xff0c;看…

【css】用css样式快速写右上角badge徽标,颜色设置为渐变色

先看效果展示&#xff0c;已公开显示在图片卡片的右上角。 首先是dom代码&#xff1a;需要两个view或者div&#xff0c;public-badge是“已公开”那个矩形&#xff0c;show-signal是右边那个下三角&#xff0c;也就是阴影部分&#xff0c;这样看起来比较有立体感。 <view…

虚拟化技术及实时虚拟化概述

版权声明&#xff1a;本文为本文为博主原创文章&#xff0c;未经本人同意&#xff0c;禁止转载。如有问题&#xff0c;欢迎指正。博客地址&#xff1a;https://www.cnblogs.com/wsg1100/ 文章目录 一、前言二、分时系统三、虚拟化介绍四、虚拟化实现方式及分类模拟器Type2虚拟化…

欧姆龙PLC联网

一、设备信息确认 左上角的为PLC型号,如图该PLC型号为CP1H,不同型号的欧姆龙PLC通讯方面有什么差别呢? 通讯能力和方式不同: 有些型号PLC自带网口,有些则需要扩展(上图中右侧的两个红框内为后扩展的通讯口,扩展模块可以随意组合双网口,双232串口,双485串口都可以)…

JDBC编程连接MySQL数据库遇到的两个错误

在进行java与MySQL数据库进行连接的时候我遇到了两个报错&#xff0c;在一开始的时候遇到的报错是Access denied for user yulinlocalhost (using password: YES)&#xff0c;此时我在网络上搜索发现是密码出现错误的问题&#xff08;出现该问题确实是密码错误&#xff09;&…

【DevOps】Atlassian插件开发指南

本文以Bamboo插件开发为例&#xff0c;记录一下插件开发过程。 一、简介 Atlassian Bamboo 6.9.1 是一款持续集成和持续交付&#xff08;CI/CD&#xff09;工具&#xff0c;支持使用插件扩展其功能。如果需要开发自己的 Bamboo 插件并添加到 Bamboo 中&#xff0c;则可以参考…

设计模式——享元模式

享元模式 定义 享元模式&#xff08;Flyweight Pattern&#xff09;是池技术的重要实现方式。 使用共享对象可以有效地支持大量的细粒度对象。 优缺点、应用场景 优点 可以大大减少应用程序创建对象的数量&#xff0c;降低程序内存占用。 缺点 提高了系统的复杂度&…

spring-IOC

IOC容器 简介 IoC(Inversion of Control)控制反转&#xff0c;是一种基于面向对象编程法则的设计思想&#xff0c;它设计出的程序具有松耦合、更优良的特点。 IoC容器是Spring框架中重要的核心组件之一&#xff0c;贯穿了Spring从出生到成长的整个过程&#xff0c;Spring通过I…