RxSwift I:开始学习 RxSwift

栏目: Swift · 发布时间: 7年前

内容简介:RxSwift 是一个组合异步和事件驱动编程的库,通过使用可观察序列和功能样式运算符来,从而允许通过调度程序进行参数化执行。RxSwift 在本质上简化了开发异步程序,允许代码对新数据作出反应,并以顺序和孤立的方式处理它。

RxSwift I:开始学习 RxSwift

RxSwift 是一个组合异步和事件驱动编程的库,通过使用可观察序列和功能样式运算符来,从而允许通过调度程序进行参数化执行。

RxSwift 在本质上简化了开发异步程序,允许代码对新数据作出反应,并以顺序和孤立的方式处理它。

介绍异步编程

RxSwift I:开始学习 RxSwift

使用 Cocoa 和 UIKit 异步的 API 的问题在于:复杂的异步代码变得非常难写,部分原因是苹果 SDK 提供的 API 种类繁多。

异步编程词汇表:

  1. 状态(State),具体地说,共享可变状态
  2. 命令式程序设计
  3. 副作用
  4. 宣告式編程:允许你定义行为片断, RxSwift 会在有相关事件的时候运行这些行为,并为它们提供一个不可变的,孤立的数据输入。
  5. 响应式系统(Reactive systems)

反应式系统是一个相当抽象的术语,它涵盖了 Web 或 iOS 应用程序,它们显示了大多数或全部以下特性:

  • 反应(Responsive):始终保持 UI 更新,代表了最新的应用程序状态。
  • 能復原的(Resilient):每个行为被隔离开来,并提供可恢复的错误恢复。
  • 灵活的(Elastic): 该代码处理不同的工作负载,通常实现诸如懒惰驱动数据收集、事件节流和资源共享等特性。
  • 消息驱动(Message driven):组件使用基于消息的通信来提高可重用性和隔离性,解耦生命周期和类的实现。

RxSwift 基础

RxSwift I:开始学习 RxSwift

这个标志是一只电鳗。(Rx 项目曾经被称为 Volta)

RxSwift 是微软开源的 ReactiveX 的 Swift 语言的实现。

RxSwift在传统的 Cocoa 编程和纯函数编程之间找到了最佳位置。它允许您对事件作出反应, 方法是使用不可变的代码定义以确定性的、可组合的方式处理异步输入部分。

Rx 代码的三个组成部分是 observables, operators 和 schedulers

Observable 类提供了 Rx 代码的基础:异步产生一系列事件的能力,它可以“携带”数据的不可变快照。简单来说,它允许类在一段时间内订阅其他类发出的值。

ObservableType 协议 (Observable 需要符合的) 非常简单。可观测的可能发出 (并且观察员能接受) 仅三类型事件:

- next 下一个事件: “携带” 最新 (或 “下一个 “) 数据值的事件。这是观察者 “接收” 值的方式。

- completed 已完成的事件: 此事件以成功终止事件序列。这意味着可观察的完成其生命周期成功, 不会发出任何其他事件。

- error 错误事件: 可观察的终止带有错误, 不会发出其他事件.

两种不同的可观测序列: finite 和 infinite。

由于它们是高度解耦和可组合的, 所以这些方法通常称为运算符。比如 filter。

运算符也是高度可组合的,它们总是把数据作为输入并输出它们的结果,所以你可以用许多不同的方式轻松地将它们链接起来,实现比单个操作员自己能做的更多的事情。

调度程序是 dispatch queues 的 Rx 等价物。

RxSwift 将充当你的订阅(在左手边下面)和调度器(在右手边)之间的调度器,将工件发送到正确的上下文,并无缝地允许它们与彼此的输出一起工作。

RxSwift I:开始学习 RxSwift

要读取此关系图, 请在不同的计划程序中按预定的顺序 (1、2、3、…) 来执行彩色作品。例如:

·蓝色网络订阅以在基于自定义 NSOperation 的计划程序上运行的一段代码 (1) 开始。

·数据输出块作为下一个块 (2) 的输入, 它运行在一个不同的调度程序上, 它位于并发后台 GCD 队列中。

·最后, 在主线程调度程序上计划最后一块蓝色代码 (3), 以便用新数据更新 UI。

App architecture 应用的架构

值得一提的是,RxSwift 并没有以任何方式改变应用程序的架构;它主要处理事件、异步数据序列和通用通信协议。

通过在苹果开发文档中实现 MVC 体系结构,可以创建具有 Rx 的应用程序。如果你喜欢的话,你也可以选择实现 MVP 架构或 MVVM。RxSwift 也可以帮你实现自己的单向数据架构。

微软的 MVVM 架构是专门针对在平台上创建的事件驱动软件开发的,该平台提供数据绑定。RxSwift 和 MVVM 很好地结合在一起,在这本书的末尾,你会看到这个模式以及如何用 RxSwift 来实现它。

MVVM 和 RxSwift 结合在一起的原因是,ViewModel 允许您公开可观察的(Observable )属性,这些属性可以直接绑定到 View Controller 代码中的 UIKIT 控件。这使得绑定模型数据到 UI 非常简单地表示和编码:

RxSwift I:开始学习 RxSwift

本书中的所有其他示例都使用 MVC 架构来保持示例代码简单易懂。

RxCocoa

RxSwift 是通用 Rx API 的实现。因此,它不涉及任何 Cocoa 或 UIKit 类。

RxCocoa 是 RxSwift 的同伴库,所有的类都有助于 UIKit 和 Cocoa 的开发。除了具有一些高级类之外,RxCocoa 还为许多 UI 组件添加了响应式扩展,以便您可以订阅不同的 UI 事件。

例如,使用 RxCocoa 订阅 UISwitch 的状态变化是非常容易的,例如:

toggleSwitch.rx.isOn
	.subcribe(onNext: {enabled in
    	print(enabled ? "it's ON" : "it's OFF")
	})

RxCocoa adds the rx.isOn property (among others) to the UISwitch class so you can subscribe to generally useful event sequences.

RxCocoa 将 rx.isOn 属性(其中之一)添加到 UISwitch 类,这样您就可以订阅通常有用的事件序列。

RxSwift I:开始学习 RxSwift

安装

官方 git: https://github.com/ReactiveX/RxSwift

使用 CocoaPods 或者 Carthage 均很方便集成 RxSwift。

社区

RxSwift 社区非常友好,思想开放,并且热衷于讨论模式,常用技巧或互相帮助。

更多的 Rx 库和实验,像雨后春笋一样的涌现,可以在这里找到: https://github.com/RxSwiftCommunity

可能最好的方式来满足许多对 RxSwift 感兴趣的人,这是 Slack 的频道: http://rxswift-slack.herokuapp.com

Slack 频道拥有约 5000 名成员! 日常的主题包括:互相帮助,讨论 RxSwift 或其同伴库的潜在新功能,以及共享 RxSwift 博客文章和会议讲座。

第二章:Observables

Observable 是什么

Observable、observable sequence 和 sequence 在 Rx 都是一个意思。或者在其他 Rx 实现中称之为 stream。

最好称之为 Observable,不过翻译过来还是序列顺口些。

Observable 的生命周期

  • observable 发出包含元素的 next 事件。 它可以继续这样做,直到它:
  • …发出 error 事件并终止,或
  • …发出 completed 事件并终止。
  • 一旦 observable 被终止,它不能再发出事件。

将这种概念化的最佳方法之一是使用弹珠图( Marble Diagrams 基于时间轴上绘制的值)。

RxSwift I:开始学习 RxSwift

RxSwift I:开始学习 RxSwift

创建Observable

类型擦除的 ObservableType,也就是 Swift 中的泛型。

它代表了一种推式风格队列。

let observable: Observable = Observable .just(one)

订阅Observable

订阅 observable sequence 的事件处理程序。

func subscribe(_ on: @escaping (RxSwift.Event<Self.E>) -> Swift.Void) -> Disposable

let one = 1
let two = 2
let three = 3

let observable = Observable.of(one, two, three)

observable.subscribe(onNext: { element in
    print(element)
})

在指定的范围内生成一个整数的 observable sequence,使用指定的 scheduler 生成和发送观察者消息。

static func range(start: Self.E, count: Self.E, scheduler: ImmediateSchedulerType = default) -> RxSwift.Observable<Self.E>
let observable = Observable<Int>.range(start: 1, count: 10)
observable
    .subscribe(onNext: { i in
        let n = Double(i)
        let fibonacci = Int(((pow(1.61803, n) - pow(0.61803, n)) /
            2.23606).rounded())
        print(fibonacci)
    })

Disposing and terminating

请记住, 在收到订阅之前, Observable 的内容不会执行任何事情。它是触发一个 Observable 的开始发出事件的订阅, 直到它发出. error 或.completed 完成事件并终止。您可以通过取消对它的订阅来手动终止 Observable。

subscription.dispose()

Managing each subscription individually would be tedious, so RxSwift includes a DisposeBag type. A dispose bag holds disposables — typically added using the .disposed(by:) method — and will call dispose() on each one when the dispose bag is about to be deallocated.

单独管理每个订阅将是单调乏味的, 因此 RxSwift 引入 DisposeBag 类型。DisposeBag 持有 disposable 协议的对象,通常是使用.disposed(by:) 方法, 并将调用 dispose(), 当 DisposeBag 即将 deallocated。

let observable = Observable.of("A", "B", "C")

let subscription = observable.subscribe { event in
    print(event)
}
subscription.dispose()

create 操作符接受一个名为 subscribe 的参数。 它的工作是提供对可观察对象进行调用订阅的实现。 换句话说,它定义了将发送给订阅者的所有事件。

static func create(_ subscribe: @escaping (AnyObserver<String>) -> Disposable) -> Observable<String>

Creating observable factories

可以创建一个可观察的工厂,向每个订阅者发布一个新的 Observable,而不是创建一个等待订阅者的 Observable。

static func deferred(_ observableFactory: @escaping () throws -> Observable<Int>) -> Observable<Int>
let disposeBag = DisposeBag()

var flip = false

let factory: Observable<Int> = Observable.deferred {
    flip = !flip

    if flip {
        return Observable.of(1, 2, 3)
    } else {
        return Observable.of(4, 5, 6)
    }
}

for _ in 0...3 {
    factory.subscribe(onNext: {
        print($0, terminator: "")
    })
        .disposed(by: disposeBag)

    print()
}

Using Traits

Traits是具有比常规 Observable 更窄的行为集合的一种 Observable。它们的使用是可选的;您可以使用任何可能使用性状的规则观察。他们的目的是提供一种给你的 API 或者代码的读者更清楚地表达意图的方式。使用 Traits 暗示的上下文可以帮助您使代码更直观。

RxSwift 中有三种 Traits: Single, Maybe 和 Completable.

Singles 将发出.success(value)或.error 事件。 .success(value)实际上是.next 和.completed 事件的组合。 这对于一次性进程非常有用,它可以成功并产生一个值或失败,例如下载数据或从磁盘加载数据。

下面的例子是读取 Copyright.txt 的文件内容:

let disposeBag = DisposeBag()

enum FileReadError: Error {
    case fileNotFound, unreadable, encodingFailed
}

func loadText(from filename: String) -> Single<String> {
    return Single.create { single in
        let disposable = Disposables.create()

        guard let path = Bundle.main.path(forResource: filename, ofType: "txt") else {
            single(.error(FileReadError.fileNotFound))
            return disposable
        }

        guard let data = FileManager.default.contents(atPath: path) else {
            single(.error(FileReadError.unreadable))
            return disposable
        }

        guard let contents = String(data: data, encoding: .utf8) else {
            single(.error(FileReadError.encodingFailed))
            return disposable
        }

        single(.success(contents))

        return disposable
    }
}

loadText(from: "Copyright")
    .subscribe {
        switch $0 {
        case .success(let string):
            print(string)
        case .error(let error):
            print(error)
        }
    }
    .disposed(by: disposeBag)

Perform side effects

do操作员允许您插入副作用,处理程序执行过程中并不会以任何方式更改发出的事件的操作。

Print debug info

debug运算符, 它将打印 observable 的每个事件的信息。

第三章:Subjects

Subject 是什么

既可以作为发射者,也可以作为观察者,这就是所谓的 Subjects。

RxSwift 中有四个 subject 类型:

  1. PublishSubject: 开始为空, 只向订阅者发出新元素.
  2. BehaviorSubject: 从初始值开始, 将其重播或将最新的元素给新订阅者.
  3. ReplaySubject: 用缓冲区大小, 并将保持元素的缓冲区大小, 并将其重播到新订阅者.
  4. Variable:包装一个 BehaviorSubject,将其当前值保存为状态,并只将最新 / 初始值重播给新订阅者。

PublishSubject

/// Represents an object that is both an observable sequence as well as an observer.
///
/// Each notification is broadcasted to all subscribed observers.
public final class PublishSubject<Element>

当你只是想让订阅者只接受在订阅的时候以后发生的新的事件,直到他们 unsubscribe,或者 subject 已经 terminated 以.completed 或.error 事件的方式,PublishSubject 就可以派上用场。

BehaviorSubject

/// Represents a value that changes over time.
///
/// Observers can subscribe to the subject to receive the last (or initial) value and all subsequent notifications.
public final class BehaviorSubject<Element>

当您希望使用最新数据预先填充 View 时, BehaviorSubject 非常有用。例如, 可以将用户详情页中的控件绑定到 BehaviorSubject, 以便在应用程序获取新数据时, 可以使用最新值来预先填充显示。

RxSwift I:开始学习 RxSwift

ReplaySubject

/// Represents an object that is both an observable sequence as well as an observer.
///
/// Each notification is broadcasted to all subscribed and future observers, subject to buffer trimming policies.
public class ReplaySubject<Element>

ReplaySubject 将临时缓存, 或缓冲区, 它们发出的最新元素, 由您选择的 specified 大小决定。然后, 他们会将该缓冲区重播到新订阅者。

Variable

/// Variable is a wrapper for `BehaviorSubject`.
///
/// Unlike `BehaviorSubject` it can't terminate with error, and when variable is deallocated
/// it will complete its observable sequence (`asObservable`).
///
/// **This concept will be deprecated from RxSwift but offical migration path hasn't been decided yet.**
/// https://github.com/ReactiveX/RxSwift/issues/1501
///
/// Current recommended replacement for this API is `RxCocoa.BehaviorRelay` because:
/// * `Variable` isn't a standard cross platform concept, hence it's out of place in RxSwift target.
/// * It doesn't have a counterpart for handling events (`PublishRelay`). It models state only.
/// * It doesn't have a consistent naming with *Relay or other Rx concepts.
/// * It has an inconsistent memory management model compared to other parts of RxSwift (completes on `deinit`).
///
/// Once plans are finalized, official availability attribute will be added in one of upcoming versions.

public final class Variable<Element> {

Variable 封装 BehaviorSubject 并将其当前值存储为状态。您可以通过其 value 属性访问当前值,并且,与一般的其他 Subject 和 Observable 不同,您还可以使用 value 属性将新元素设置到变量上。换句话说,你不需要使用 onNext(_:)。

let variable = Variable("Initial value")
let disposeBag = DisposeBag()
variable.value = "New initial value"
variable.asObservable()
  .subscribe {
    print(label: "1)", event: $0)
  }
  .disposed(by: disposeBag)

variable.value = "1"
variable.asObservable()
  .subscribe {
    print(label: "2)", event: $0)
  }
  .disposed(by: disposeBag)

variable.value = "2"

第四章:Observables 和 Subjects 的实践

本章重点是在一个完整的应用开发中使用 RxSwift,一步一步的学会如何把概念应用到实际项目中。

在 view controller 中使用 variable

override func viewDidLoad() {
    super.viewDidLoad()

    images.asObservable()
      .subscribe(onNext: { [weak self] photos in
        guard let preview = self?.imagePreview else { return }
        preview.image = UIImage.collage(images: photos,
                                        size: preview.frame.size)
      })
      .disposed(by: bag)

    images.asObservable()
      .subscribe(onNext: { [weak self] photos in
        self?.updateUI(photos: photos)
      })
      .disposed(by: bag)
  }

使用 variable 在 view controller 之间传值

view controller 之间传值可以通过 delegate,但是用 subject 更好。

RxSwift I:开始学习 RxSwift

RxSwift I:开始学习 RxSwift

@IBAction func actionAdd() {
    let photosViewController = storyboard!.instantiateViewController(
      withIdentifier: "PhotosViewController") as! PhotosViewController

    photosViewController.selectedPhotos
      .subscribe(onNext: { [weak self] newImage in
        guard let images = self?.images else { return }
        images.value.append(newImage)
      }, onDisposed: {
          print("completed photo selection")
      })
      .disposed(by: bag)

    navigationController!.pushViewController(photosViewController, animated: true)
  }

封装已有 API 为 Observable

import Foundation
import UIKit
import Photos

import RxSwift

class PhotoWriter {
  enum Errors: Error {
    case couldNotSavePhoto
  }

  static func save(_ image: UIImage) -> Observable<String> {
    return Observable.create({ observer in
      var savedAssetId: String?
      PHPhotoLibrary.shared().performChanges({
        let request = PHAssetChangeRequest.creationRequestForAsset(from: image)
        savedAssetId = request.placeholderForCreatedAsset?.localIdentifier
      }, completionHandler: { success, error in
        DispatchQueue.main.async {
          if success, let id = savedAssetId {
            observer.onNext(id)
            observer.onCompleted()
          } else {
            observer.onError(error ?? Errors.couldNotSavePhoto)
          }
        }
      })
      return Disposables.create()
    })
  }
}

RxSwift traits 实践

  1. Single 只有 .success.error 两种事件 。适合作为封装网络接口的返回值,要么成功,要么失败。 RxSwift I:开始学习 RxSwift
PhotoWriter.save(image)
      .asSingle()
      .subscribe(onSuccess: { [weak self] id in
        self?.showMessage("Saved with id: \(id)")
        self?.actionClear()
        }, onError: { [weak self] error in
          self?.showMessage("Error", description: error.localizedDescription)
      })
      .disposed(by: bag)

2 . Maybe 有三种事件: .success.completed.error

和 Single 一样,你既可以通过 Maybe.create({...}) 直接创建,也可以使用 .asMaybe()

RxSwift I:开始学习 RxSwift

  1. Completable 只有 .completed.error 两种事件。适合只关心是否完成,而不需要传递 value 的情况。使用方法: Completable.create({...})

RxSwift I:开始学习 RxSwift


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Python for Everyone

Python for Everyone

Cay S. Horstmann、Rance D. Necaise / John Wiley & Sons / 2013-4-26 / GBP 181.99

Cay Horstmann's" Python for Everyone "provides readers with step-by-step guidance, a feature that is immensely helpful for building confidence and providing an outline for the task at hand. "Problem S......一起来看看 《Python for Everyone》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

在线进制转换器
在线进制转换器

各进制数互转换器

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码