Rxswift使用


title: Rxswift使用
date: 2019-12-11 16:58:26
tags:

一、函数响应式编程

1.函数式编程

函数式编程是种编程范式,它需要我们将函数作为参数传递,或者作为返回值返还。我们可以通过组合不同的函数来得到想要的结果

let gradeThreeTwoStudent = students.filter { (student) -> Bool in
    return student.grade == 3 && student.className == 2
}

*筛选出3年级2班的学生

gradeThreeTwoStudent.filter { (stu) -> Bool in
    return stu.score > 90
}.map { (stu) -> Parent in
    return stu.parent!
}.forEach { (parent) in
    parent.recevieReward()
}

*通过filter筛选出3年级2班成绩大于90分的学生

*用map转换为学生家长,最后用forEach让每个家长上台领奖。

2.函数响应式编程

a=b+c表示将表达式的结果赋给 a,而之后改变 b 或 c的值不会影响 a。但在响应式编程中,a的值会随着 b或 c的更新而更新。a=b+c声明的是一种绑定关系。

二、Rxswift核心

1.Observable - 可监听序列

*创建可监听序列

let numbers:Observable<Int> = Observable.create { oberver -> Disposable in
    oberver.onNext(1)
    oberver.onNext(2)
    oberver.onCompleted()
    return Disposables.create()
}

*框架已经帮我们创建好了许多常用的序列。例如:button的点击,textField的当前文本,switch的开关状态,slider的当前数值等等。

*可以用这种方法封装闭包回调

let json: Observable<JSON> = Observable.create { (obsever) -> Disposable in
  let task = URLSession.shared.dataTask(with: URL(string: urlStr)!) { (data, response, error) in
      guard error == nil else {
          obsever.onError(error!)
          return
      }

      guard let data = data, let jsonObject = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) else {
          obsever.onError(error!)
          return
      }

      obsever.onNext(jsonObject)
      obsever.onCompleted()
  }

  task.resume()
  return Disposables.create {
      task.cancel()
  }
}

*用 subscribe 方法来响应这个请求的结果

json.subscribe(onNext: { (json) in
    print("取得json\(json)")
}, onError: { (error) in
    print("error\(error.localizedDescription)")
}, onCompleted: {
    print("任务成功完成")
}) { 
    print("DisposeBag")
}
.disposed(by: disposeBag)

*框架还给我们提供了下面这些序列类型供使用

  • Single

  • Completable

  • Maybe

  • Driver

  •  let textInput = textfield.rx.text.orEmpty.asDriver().throttle(0.3)
    
     //driver
      //不会产生 error 事件
      //一定在 MainScheduler 监听(主线程监听)
      //共享附加作用
      
      //throttle
      //指定了两秒钟,所以在0.3以内,只接收了第一条和最新的数据。
      //适用于:输入框搜索限制发送请求。
      //取出用户输入稳定后的内容
      
      textInput.drive(tf2.rx.text).disposed(by: disposeBag)
    
  • Signal

  • ControlEvent

2.Observer - 观察者

观察者 是用来监听事件,然后它需要这个事件做出响应。例如:弹出提示框就是观察者*,它对点击按钮这个事件做出响应。

*创建观察者最直接的方法就是在 Observablesubscribe 方法后面描述,事件发生时,需要如何做出响应。而观察者就是由后面的 onNextonErroronCompleted的这些闭包构建出来的。

*AnyObserver 可以用来描叙任意一种观察者。

*打印网络请求结果:

URLSession.shared.rx.data(request: URLRequest(url: URL(string: urlStr)!)).subscribe(onNext: { (data) in
    print("取得json\(data)")
}, onError: { (error) in
    print("error\(error.localizedDescription)")
}, onCompleted: {
    print("任务成功完成")
}) {
    print("Dispose")
}.disposed(by: disposeBag)

用AnyObserver:

let observer: AnyObserver<Data> = AnyObserver {(event) in
  switch event {
  case .next(let data):
      print("Data Task Success with count: \(data.count)")
  case .error(let error):
      print("Data Task Error: \(error)")
  case .completed:
      print("完成")
  }
}

URLSession.shared.rx.data(request: URLRequest(url: URL(string: urlStr)!)).subscribe(observer).disposed(by: disposeBag)


UI 观察者,所以它在响应事件时,只会处理 next 事件,并且更新 UI 的操作需要在主线程上执行。

因此一个更好的方案就是使用 Binder

用户输入超过5个字符隐藏提示语labelUserNameUnValid

let userNameValid = textFieldUserName.rx.text.orEmpty.map{$0.count >= 5}.share(replay: 1)
userNameValid.bind(to: labelUserNameUnValid.rx.isHidden).disposed(by: disposeBag)


*Binder

let userNameValid = textFieldUserName.rx.text.orEmpty.map{$0.count >= 5}.share(replay: 1)
        
let observer: Binder<Bool> = Binder(labelUserNameUnValid) {(view,isHidden) in
    view.isHidden = isHidden
}
userNameValid.bind(to: observer).disposed(by: disposeBag)


*很多UI控件的特性即可以是可监听序列又可以是观察者,例如:

textField的当前文本

switch的开关状态

segmentedControl的选中索引号

datePicker的选中日期

3.操作符

操作符可以帮助大家创建新的序列,或者变化组合原有的序列,从而生成一个新的序列。

a.filter-筛选

*仅仅发出 Observable 中通过判定的元素

Observable.of(1, 3, 20, 15, 30, 50).filter {$0 > 10}.subscribe(onNext: {print($0)}).disposed(by: disposeBag)


b.map - 转换

*map 操作符将源 Observable 的每个元素应用你提供的转换方法,然后返回含有转换结果的 Observable

Observable.of(1,2,3,4,5,6,7).map {$0 * 10}.subscribe(onNext: {print($0)}).disposed(by: disposeBag)


输出10 20 30 40 50 60 70

c.zip - 配对

*可以用 zip 来合成一个新的序列

        let subject1 = PublishSubject<String>()
        let subject2 = PublishSubject<Int>()
        Observable.zip(subject1, subject2) { (str, intValue) -> String in
            return "\(intValue)-\(str)"
        }.subscribe { (str) in
            print(str)
        }.disposed(by: disposeBag)
        
        subject1.onNext("A")
        subject2.onNext(1)
        
        subject1.onNext("B")
        subject2.onNext(2)
        
        subject1.onNext("C")
        subject2.onNext(3)
        
        subject1.onNext("D")
        subject2.onNext(4)

*输出
next(1-A)
next(2-B)
next(3-C)
next(4-D)

注:Subject是一种桥梁和代理,在ReactiveX的一些实现中,它既可以当作observer也可以当做Observable,PublishSubject是其中一种。

*zip 常常用在整合网络请求上
*同时发送两个请求,只有当两个请求都成功后,再将两者的结果整合起来继续往下处理

let json1: Observable<JSON> = Observable.create { (obsever) -> Disposable in
            let task = URLSession.shared.dataTask(with: URL(string: "https://ppt.szwdcloud.com/?info=0&furl=http://szwd.oss-cn-qingdao.aliyuncs.com/files/f1f7e562bbb84730ad2fa6d6c48a2bff.pptx")!) { (data, response, error) in
                guard error == nil else {
                    obsever.onError(error!)
                    return
                }
                
                guard let data = data, let jsonObject = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) else {
                    obsever.onError(error!)
                    return
                }
                
                obsever.onNext(jsonObject)
                obsever.onCompleted()
            }
            
            task.resume()
            return Disposables.create {
                task.cancel()
            }
        }
        
        let json2: Observable<JSON> = Observable.create { (obsever) -> Disposable in
            let task = URLSession.shared.dataTask(with: URL(string: "https://ppt.szwdcloud.com/?info=0&furl=http://szwd.oss-cn-qingdao.aliyuncs.com/files/f1f7e562bbb84730ad2fa6d6c48a2bff.pptx")!) { (data, response, error) in
                guard error == nil else {
                    obsever.onError(error!)
                    return
                }
                
                guard let data = data, let jsonObject = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) else {
                    obsever.onError(error!)
                    return
                }
                
                obsever.onNext(jsonObject)
                obsever.onCompleted()
            }
            
            task.resume()
            return Disposables.create {
                task.cancel()
            }
        }
        
        Observable.zip(json1, json2) { (object1, object2) -> (object1: Any, object2: Any) in
            return (object1, object2)
        }.subscribe { (object) in
            print(object)
        }.disposed(by: disposeBag)



4.Disposable - 可被清除的资源

*一个序列如果发出了 error 或者 completed 事件,那么所有内部资源都会被释放。如果你需要提前释放这些资源或取消订阅的话,那么你可以对返回的 可被清除的资源(Disposable) 调用 dispose 方法
*调用 dispose 方法后,订阅将被取消,并且内部资源都会被释放。通常情况下,你是不需要手动调用 dispose 方法的
*可以使用 清除包(DisposeBag) 或者 takeUntil 操作符 来管理订阅的生命周期

a.清除包(DisposeBag)

var disposeBag = DisposeBag()

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    textField.rx.text.orEmpty
        .subscribe(onNext: { text in print(text) })
        .disposed(by: self.disposeBag)
}

*当退出页面时, ViewController 就被释放,disposeBag 也跟着被释放了
*当 清除包 被释放的时候,清除包 内部所有 可被清除的资源(Disposable) 都将被清除

b.takeUntil

let userNameValid = textFieldUserName.rx.text.map{$0?.count ?? 0 >= 5}.share(replay: 1)
_ = userNameValid.takeUntil(self.rx.deallocated).bind(to: labelUserNameUnValid.rx.isHidden)

*这将使得订阅一直持续到控制器的 dealloc 事件产生为止

5.Schedulers - 调度器

*Schedulers 是 Rx 实现多线程的核心模块,它主要用于控制任务在哪个线程或队列运行。

let json: Observable<JSON> = Observable.create { (obsever) -> Disposable in
            let task = URLSession.shared.dataTask(with: URL(string: "http://ppt.szwdcloud.com/?info=0&furl=http://szwd.oss-cn-qingdao.aliyuncs.com/files/f1f7e562bbb84730ad2fa6d6c48a2bff.pptx")!) { (data, response, error) in
                guard error == nil else {
                    obsever.onError(error!)
                    return
                }
                
                guard let data = data, let jsonObject = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) else {
                    obsever.onError(error!)
                    return
                }
                
                obsever.onNext(jsonObject)
                obsever.onCompleted()
            }
            
            task.resume()
            return Disposables.create {
                task.cancel()
            }
        }


        json.subscribeOn(ConcurrentDispatchQueueScheduler(qos: .userInitiated)).observeOn(MainScheduler.instance).subscribe(onNext: { (data) in
            
        }, onError: { (error) in
            
        }, onCompleted: {
            
        }) {
            
        }.disposed(by: disposeBag)



*用 subscribeOn 来决定数据序列的构建函数在哪个 Scheduler 上运行
*用 observeOn 来决定在哪个 Scheduler 监听这个数据序列
*可以先用 subscribeOn 切到后台去发送请求并解析数据,最后用 observeOn 切换到主线程更新页面。


MainScheduler

MainScheduler 代表主线程。如果你需要执行一些和 UI 相关的任务,就需要切换到该 Scheduler 运行。

MainScheduler.instance


SerialDispatchQueueScheduler

SerialDispatchQueueScheduler 抽象了串行 DispatchQueue。如果你需要执行一些串行任务,可以切换到这个 Scheduler 运行。

ConcurrentDispatchQueueScheduler

ConcurrentDispatchQueueScheduler 抽象了并行 DispatchQueue。如果你需要执行一些并发任务,可以切换到这个 Scheduler 运行。

OperationQueueScheduler

OperationQueueScheduler 抽象了 NSOperationQueue。

它具备 NSOperationQueue 的一些特点,例如,你可以通过设置 maxConcurrentOperationCount,来控制同时执行并发任务的最大数量。

6.错误处理

*一旦序列里面产出了一个 error 事件,整个序列将被终止

a.retry - 重试

let rxJson: Observable<JSON> = ...

rxJson
    .retry(3)
    .subscribe(onNext: { json in
        print("取得 JSON 成功: \(json)")
    }, onError: { error in
        print("取得 JSON 失败: \(error)")
    })
    .disposed(by: disposeBag)


*在一个Observable后面加上retry,当这个Observable出错的时候订阅方不会收到.error事件,而是重新订阅这个Observable,以上代码重试3次之后,订阅者才会收到error

b. retrywhen

*这个操作符主要描述应该在何时重试,并且通过闭包里面返回的 Observable 来控制重试的时机

   json.retryWhen({ (rxError) -> Observable<Int> in
        return Observable.timer(5, scheduler: MainScheduler.instance)
    }).subscribe(onNext: { (data) in

    }, onError: { (error) in
        print(error.localizedDescription)
    }, onCompleted: {

    }) {

    }.disposed(by: disposeBag)



*rxError也就是所产生错误的序列,然后返回值是一个 Observable。当这个返回的 Observable 发出一个元素时,就进行重试操作。当它发出一个 error 或者 completed 事件时,就不会重试
*以上代码是5秒后重试一次

json.retryWhen({ (rxError) -> Observable<Int> in
            
            //                   return Observable.timer(5, scheduler: MainScheduler.instance)
            return rxError.flatMap { (error) -> Observable<Int> in
                self.count += 1
                guard self.count < 5 else {
                    return Observable.error(error)
                }
                
                return Observable<Int>.timer(5, scheduler: MainScheduler.instance)
            }
        }).subscribe(onNext: { (data) in
            
        }, onError: { (error) in
            print(error.localizedDescription)
        }, onCompleted: {
            
        }) {
            
        }.disposed(by: disposeBag)



*以上实现如果重试超过 5 次,就将错误抛出。如果错误在 4 次以内时,就等待 5 秒后重试

c.catch - 恢复

*catchError 可以在错误产生时,用一个备用元素或者一组备用元素将错误替换掉

三、UI控件的使用

具体见demo

四、Rxswift+MVVM

具体见demo

五、参考链接

https://beeth0ven.github.io/RxSwift-Chinese-Documentation/

https://winterliao.github.io/2018/08/14/Rxswift使用/

https://www.jianshu.com/p/646a289cddc7

https://www.hangge.com/blog/cache/detail_2037.html

https://www.jianshu.com/p/a83eba5e7b8c

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值