RxSwift 介绍与简单使用

文章目录

  • 一、 Rx 介绍
    • 1、什么是Rx
    • 2、RxSwift
    • 3、RxCocoa
  • 二、Rx 常见用法
    • 1、给 button 添加点击事件(RxCocoa)
    • 2、事件 + bind + combine
    • 3、遵循代理并实现
    • 4、闭包回调
    • 5、通知
    • 6、多任务依赖关系管理
    • 7、多任务异步并行
  • 三、DisposeBag(清除包)介绍:
  • 四、函数式编程介绍
  • 参考文献:

一、 Rx 介绍

1、什么是Rx

Rx 是 ReactiveX 的缩写,简单来说就是基于异步 Event 序列的响应式编程。Rx 可以简化异步编程方法,并提供更优雅的数据绑定,让我们可以时刻响应新的数据的同时,顺序地处理他们。

2、RxSwift

在编程过程中,我们经常需要去检测某些值的变化(如:textfield 输入变化),然后进行相应的处理。RxSwift 的出现,让程序里的时间传递响应方法做到了统一,将之前常用的事件传递方法(ru delegate、notification、target-action等),全部替换成 Rx 的“信号链”方式。

在 MVVM 的开发模式下,可以通过 RxSwift 获得更加方便的数据绑定方法,让 MVVM 更加灵活轻便。

3、RxCocoa

RxCocoa 是 RxSwift 的一部分,主要是 UI 相关的 Rx 封装。RxCocoa 实现了很多组件的绑定,协助开发者把值和控件进行绑定,避免代码中产生大量的通知、代理、数据修改等代码。也可以监听 delegate,无需把控件创建和 delegate 处理分开。

二、Rx 常见用法

1、给 button 添加点击事件(RxCocoa)

在之前,当我们需要给button添加一个点击事件的时候,得这么干:

button.addTarget(self, action: #selector(buttonClicked), for: .touchUpInside)

@objc func buttonClicked() {
  ...
}

在使用 RxCocoa 之后,只需这样写:

button.rx.tap
  .subscribe { _ in // 订阅点击事件信号
    print("clicked button")
  }
  .disposed(by: disposeBag) // 
  
// RxSwift tap 源码
extension Reactive where Base: UIButton {
    
  /// Reactive wrapper for `TouchUpInside` control event.
  public var tap: ControlEvent {
    return controlEvent(.touchUpInside)
  }
}

2、事件 + bind + combine

  • share(replay: 1):共享同一个源,不单独创建新的源,以减少不必要的开支;
  • orEmpty:将可选值的 nil  转化为空字符串输出,以减少可选类型隐式解包;
  • bind:用来将一个信号发送者和一个信号监听者绑定在一起,即有信号发送,监听者自动收到通知;
  • combine:信号融合
func rxCombine() {
  let accountValid = accountTextField.rx.text.orEmpty
    .map {
    $0.count >= 5
  }.share(replay: 1)
  // 用 accountValid 来控制用户名提示语是否隐藏以及密码输入框是否可用。shareReplay 就是让他们共享这一个源,而不是为他们单独创建新的源。这样可以减少不必要的开支。
    
  let passwordValid = passwordTextField.rx.text.orEmpty.map {
    $0.count >= 5
  }.share(replay: 1)
    
  let everythingValid = Observable.combineLatest(accountValid, passwordValid) {
    $0 && $1
  }.share(replay: 1)
    
  accountValid
    .bind(to: passwordTextField.rx.isEnabled)
    .disposed(by: disposeBag)
    
  everythingValid
    .bind(onNext: { [weak self] enable in
      if enable {
        self?.loginButton.isEnabled = true
        self?.loginButton.setTitle("can click", for: .normal)
      } else {
        self?.loginButton.isEnabled = false
        self?.loginButton.setTitle("can not click", for: .normal)
      }
    })
//    .bind(to: loginButton.rx.isEnabled)
    .disposed(by: disposeBag)
    
  loginButton.rx.tap
    .subscribe { _ in
      print("click the login button")
  }
  .disposed(by: disposeBag)
}

3、遵循代理并实现

传统代理方法实现:

class ViewController: UIViewController {
  ...
  override func viewDidLoad() {
    super.viewDidLoad()
    scrollView.delegate = self
  }
  
}

extension ViewController: UIScrollViewDelegate {

  func scrollViewDidScroll(_ scrollView: UIScrollView) {
    print("contentOffset: \(scrollView.contentOffset)")
  }
  
}

使用 Rx 实现:

class ViewController: UIViewController {
  ...
  override func viewDidLoad() {
    super.viewDidLoad()

    scrollView.rx.contentOffset
      .subscribe(onNext: { contentOffset in
        print("contentOffset: \(contentOffset)")
      })
      .disposed(by: disposeBag) // 每一个绑定是有生命周期的,并且这个绑定是可以被清除的。将每一个绑定的生命周期交给 disposeBag 管理,当 disposeBag 释放时,会自动清理 _disposables 数组中所有的绑定
  }
}

4、闭包回调

传统实现方案:

URLSession.shared.dataTask(with: URLRequest(url: url)) { (data, response, error) in
  guard error == nil else {
    print("Data Task Error: \(error!)")
    return
  }

  guard let data = data else {
    print("Data Task Error: unknown")
    return
  }

  print("Data Task Success with count: \(data.count)")
}.resume()

Rx 实现方案:

URLSession.shared.rx.data(request: URLRequest(url: url))
  .subscribe(onNext: { data in
    print("Data Task Success with count: \(data.count)")
  }, onError: { error in
    print("Data Task Error: \(error)")
  })
  .disposed(by: disposeBag)

通过 Rx 的方式,让回调变得非常的简单。

5、通知

var notificationObserver: NSObjectProtocol!

override func viewDidLoad() {
  super.viewDidLoad()

  notificationObserver = NotificationCenter.default.addObserver(forName: .UIApplicationWillEnterForeground, object: nil, queue: nil) { (notification) in
    print("Application Will Enter Foreground")
  }
}

deinit {
  NotificationCenter.default.removeObserver(notificationObserver)
}

Rx 实现方式:

override func viewDidLoad() {
  super.viewDidLoad()

  NotificationCenter.default.rx
    .notification(.UIApplicationWillEnterForeground)
    .subscribe(onNext: { (notification) in
      print("Application Will Enter Foreground")
    })
    .disposed(by: disposeBag)
}

6、多任务依赖关系管理

多任务依赖管理管理,如异步串行请求,由于业务原因,可能会存在请求依赖的场景,如:登录完成获取到token后才能请求用户信息。

func login(userName: String, password: String, completion: @escaping (_ result: Result?) -> Void) {
  DispatchQueue.global().async {
    print("normal login success")
    completion(nil)
  }
}
  
func loadUserInfo(completion: @escaping (_ result: Result?) -> Void) {
  DispatchQueue.global().async {
    print("normal load user info success")
    completion(nil)
  }
}
  
func loadRecommendsGoods(completion: @escaping (_ result: Result?) -> Void) {
  DispatchQueue.global().async {
    print("normal load recommend goods success")
    completion(nil)
  }
}

// 嵌套调用,异步串行
func normal_taskStart() {
  login(userName: "enoch", password: "11111") { [weak self] _ in
    self?.loadUserInfo { [unowned self] _ in
      self?.loadRecommendsGoods { _ in
      // do nothing
      }
    }
  }
}

Rx 实现方式

func rx_login(userName: String, password: String) -> Observable {
  let createSequence =  Observable.create { observer -> Disposable in
    DispatchQueue.global().async {
      print("rx login success")
      observer.onNext("login success")
      observer.onCompleted()
    }
      
    return Disposables.create()
  }
    
  return createSequence
}
  
func rx_loadUserInfo() -> Observable<[String : Any]> {
  let createSequence = Observable<[String : Any]>.create { observer -> Disposable in
    DispatchQueue.global().async {
      print("rx load user info success")
      observer.onNext(["name" : "enoch", "age" : 18])
      observer.onCompleted()
    }
      
    return Disposables.create()
  }
    
  return createSequence
}
  
func rx_loadRecommendsGoods() -> Observable<[String]> {
  let createSequence = Observable<[String]>.create { observer -> Disposable in
    DispatchQueue.global().async {
      print("rx load recommend goods success")
      observer.onNext(["goods1", "goods2", "goods3"])
      observer.onCompleted()
    }
      
    return Disposables.create()
  }
    
  return createSequence
}

// 异步串行调用
func rx_taskStart() {
  rx_login(userName: "enoch", password: "111111")
  .flatMap { [unowned self] _ in self.rx_loadUserInfo() }
  .flatMap { [unowned self] _ in self.rx_loadRecommendsGoods() }
  .subscribe(onNext: { goodsArray in
    print(goodsArray)
  })
  .disposed(by: disposeBag)
}

7、多任务异步并行

Rx 当中,可使用压缩信号的方式,进行多任务异步并行,示例代码如下:

func rx_zipTask() {
  Observable.zip(
    rx_login(userName: "enoch", password: "111111"),
    rx_loadUserInfo(),
    rx_loadRecommendsGoods()
  ).subscribe(onNext: { (token, userData, goodsData) in
    print("token:\(token)")
    print("user data:\(userData)")
    print("goods data:\(goodsData)")
  }, onError: { error in
    // do nothing
  })
  .disposed(by: disposeBag)
}

三、DisposeBag(清除包)介绍:

  • DisposeBag 有一个专门存放垃圾回收的 _disposables 数组;和一个表示当前对象是否被回收的属性值_isDisposed (默认是false);
  • 当 DisposeBag 的生命周期在 Viewcontroller 中结束的时候,调用自身的 deinit,随后调用 self.dispose();
  • 紧接着 会将_isDisposed 变成ture,然后循环遍历 _disposables 数组,并将所有 Disposable对象都调用 dispose() 方法进行释放
  • 每一个绑定是有生命周期的,并且这个绑定是可以被清除的。将每一个绑定的生命周期交给 disposeBag 管理,当 disposeBag 释放时,会自动清理 _disposables 数组中所有的绑定

四、函数式编程介绍

编程范式了解一下:

  • 命令式:命令式编程通过一系列改变程序状态的指令来完成计算。命令式编程模拟电脑运算,是行动导向的,关键在于定义解法,即“怎么做”,因而算法是显性而目标是隐性的;
  • 声明式:声明式编程只描述程序应该完成的任务。声明式编程模拟人脑思维,是目标驱动的,关键在于描述问题,即“做什么”,因而目标是显性而算法是隐性的;

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

函数式编程优势:

  • 减轻程序猿思考的负担,降低出错可能性,简称防秃;
  • 代码可读性高;
  • 代码更简洁;
  • 适用于并发环境;
  • 易于优化;
  • 细粒度的重用(函数级别);
  • 易于测试;

下方是简单举例:

func studentFilter() {
  let studentsInGradeThreeClassThree = allStudents()
    .filter { student -> Bool in student.grade == 3 && student.cls == 3 }
  print("三年级三班有 \(studentsInGradeThreeClassThree.count) 人")
    
  studentsInGradeThreeClassThree
    .filter { student -> Bool in student.sex == .male }
    .forEach { boy in boy.singASong() }
    
  studentsInGradeThreeClassThree
    .filter { student -> Bool in student.score > UInt(90) }
    .forEach { student in print(student.father) }
    
  studentsInGradeThreeClassThree
    .sorted { (student1, student2) -> Bool in student1.score > student2.score }
    .forEach { student in print("\(student.name): \(student.score)") }
 }

参考文献:

1、swift 官方中文文档

你可能感兴趣的:(swift,iOS开发,RxSwift,RxSwift,Swift,iOS,响应式编程,函数式编程)