代码下载

为何使用Rx

Rx支持以声明方式构建应用程序。

绑定

// 原文这里应该有纰漏,应为firstName.rx.text.orEmpty与lastName.rx.text.orEmpty否则会造成可选字符串未解包
Observable.combineLatest(firstName.rx.text, lastName.rx.text) { $0 + " " + $1 }.map { "Greetings, \($0)" }.bind(to: greetingLabel.rx.text)

也可以绑定UITableView和UICollectionView。

viewModel.rows.bind(to: resultsTableView.rx.items(cellIdentifier: "WikipediaSearchCell", cellType: WikipediaSearchCell.self)) { (_, viewModel, cell) incell.title = viewModel.titlecell.url = viewModel.url}.disposed(by: disposeBag)

官方建议总是使用.disposed(by: disposeBag)即使这对简单绑定不是必需的。

重试

如果API不会失败会很棒,但遗憾的是它们会失败。假设有一个API方法:

func doSomethingIncredible(forWho: String) throws -> IncredibleThing

如果按原样使用此函数,则在失败时很难进行重试。更不用说建模嵌套调用的复杂性。当然有可能,但代码可能包含许多不关心的瞬态,并且它不可重复使用。

理想情况下,需要捕获重试的本质,并能够将其应用于任何操作。

这是使用Rx进行简单重试的方法

doSomethingIncredible("me").retry(3)

还可以轻松创建自定义重试操作。

代理

代替冗长和难以表示的代码

public func scrollViewDidScroll(scrollView: UIScrollView) { [weak self]self?.leftPositionConstraint.constant = scrollView.contentOffset.x
}

Rx实现

self.resultsTableView.rx.contentOffset.map { $0.x }.bind(to: self.leftPositionConstraint.rx.constant)

KVO

代替:

对象销毁了然而键值观察还注册着,观测信息被泄露,甚至可能被错误地附加到其他对象上。

-(void)observeValueForKeyPath:(NSString *)keyPathofObject:(id)objectchange:(NSDictionary *)changecontext:(void *)context

使用rx.observe和rx.observeWeakly

这是他们的使用方式:

view.rx.observe(CGRect.self, "frame").subscribe(onNext: { frame inprint("Got new frame \(frame)")}).disposed(by: disposeBag)

或者

someSuspiciousViewController.rx.observeWeakly(Bool.self, "behavingOk").subscribe(onNext: { behavingOk inprint("Cats can purr? \(behavingOk)")}).disposed(by: disposeBag)

通知

代替使用:

@available(iOS 4.0, *)
public func addObserverForName(name: String?, object obj: AnyObject?, queue: NSOperationQueue?, usingBlock block: (NSNotification) -> Void) -> NSObjectProtocol

仅仅编写

NotificationCenter.default.rx.notification(NSNotification.Name.UITextViewTextDidBeginEditing, object: myTextView).map {  /*使用数据做一些事情*/ }....

瞬态

编写异步程序时,瞬态存在很多问题。典型示例是自动完成的搜索框。

如果没有Rx要编写自动完成的搜索框代码,那么可能需要解决的第一个问题是当输入abc中的c时,有一个待处理的请求ab,需要取消待处理的请求。好吧,这应该不太难解决,只需创建一个额外的变量来保持对待处理请求的引用。

下一个问题是如果请求失败,则需要执行那种混乱的重试逻辑。但是好吧,增加字段捕获需要处理的重试次数。

如果程序需要在向服务器发出请求之前等待一段时间。毕竟,因为不想有人在长时间输入时太频繁的请求服务器。可能需要添加一个计时字段?

还有一个问题是,在执行搜索时屏幕上需要显示什么,以及即使重试也会失败的情况下需要显示的内容。

写下所有这些并正确地测试它将是乏味的。这是用Rx编写的相同逻辑。

searchTextField.rx.text.throttle(.milliseconds(300), scheduler: MainScheduler.instance).distinctUntilChanged().flatMapLatest { query inAPI.getSearchResults(query).retry(3).startWith([]) // 在新的搜索中清除结果.catchErrorJustReturn([])}.subscribe(onNext: { results in// 绑定到UI}).disposed(by: disposeBag)

组合处理

假设希望在表视图中显示模糊图像。首先,应从URL中提取图像,然后解码然后模糊。

单元格退出表视图的可视区域可以取消整个过程是很好的,因为处理模糊的开销非常大。

在单元格进入可见区域后不立即开始获取图像也是很好的,因为如果用户滑动非常快,可能会有很多请求被触发和取消。

如果我们可以限制并发图像操作的数量也很好,同样因为模糊图像是一项开销非常大的操作。

这就是如何使用Rx来做到这一点:

// 这是一个概念性的解决方案
let imageSubscription = imageURLs.throttle(.milliseconds(200), scheduler: MainScheduler.instance).flatMapLatest { imageURL inAPI.fetchImage(imageURL)}.observeOn(operationScheduler).map { imageData inreturn decodeAndBlurImage(imageData)}.observeOn(MainScheduler.instance).subscribe(onNext: { blurredImage inimageView.image = blurredImage}).disposed(by: reuseDisposeBag)

此代码将完成所有这些操作,并且在imageSubscription完成处理时,它将取消所有相关的异步操作,并确保没有恶意图像绑定到UI。

聚合网络请求

如果需要触发两个请求并在两个请求完成时汇总结果,该怎么办?

zip操作符

let userRequest: Observable<User> = API.getUser("me")
let friendsRequest: Observable<[Friend]> = API.getFriends("me")Observable.zip(userRequest, friendsRequest) { user, friends inreturn (user, friends)
}
.subscribe(onNext: { user, friends in// 绑定到界面
})
.disposed(by: disposeBag)

那么,如果这些API在后台线程上返回结果,并且绑定UI必须在主线程上进行呢?可以使用observeOn。

let userRequest: Observable<User> = API.getUser("me")
let friendsRequest: Observable<[Friend]> = API.getFriends("me")Observable.zip(userRequest, friendsRequest) { user, friends inreturn (user, friends)
}
.observeOn(MainScheduler.instance)
.subscribe(onNext: { user, friends in// bind them to the user interface
})
.disposed(by: disposeBag)

还有更多使用Rx发挥作用的实际用例。

状态

允许变化的语言可以轻松访问全局状态并对其进行更改。共享全局状态的不受控制的更改很容易导致组合爆炸。

但另一方面,当以智能方式使用时,命令式语言可以编写更高效更接近硬件的代码。

处理组合爆炸的常用方法是保持状态尽可能简单,并使用单向数据流来模拟派生数据。

这是Rx真正发挥作用的地方。

Rx是函数式和命令式的最佳结合点。它能够使用不可变定义和纯函数以可靠的可组合方式处理可变状态。

那么,有哪些实际例子呢?

易于集成

如果需要创建自己的可观察对象怎么办?这很容易。这段代码来自RxCocoa,这就是需要用URLSession包装HTTP请求的全部内容

extension Reactive where Base: URLSession {public func response(request: URLRequest) -> Observable<(Data, HTTPURLResponse)> {return Observable.create { observer inlet task = self.base.dataTask(with: request) { (data, response, error) inguard let response = response, let data = data else {observer.on(.error(error ?? RxCocoaURLError.unknown))return}guard let httpResponse = response as? HTTPURLResponse else {observer.on(.error(RxCocoaURLError.nonHTTPResponse(response: response)))return}observer.on(.next(data, httpResponse))observer.on(.completed)}task.resume()return Disposables.create(with: task.cancel)}}
}

优点

简而言之,使用Rx将使代码:

  • 可组合 <- 因为Rx是组合的代名词
  • 可重用 <- 因为它是可组合的
  • 声明性 <- 因为定义是不可变的,只有数据发生变化
  • 可理解和简洁 <- 提高抽象级别并消除瞬态
  • 稳定 <- 因为Rx代码经过了彻底的单元测试
  • 状态较少 <- 因为将应用程序建模为单向数据流
  • 没有泄漏 <- 因为资源管理很容易

这并不是全部

使用Rx对尽可能多的应用程序进行建模通常是个好主意。

但是,如果不了解所有操作符以及还不存在某些操作符对特定情况进行建模,该怎么办?

好吧,所有Rx操作符都基于数学,应该是直观的。

好消息是大约10-15个操作符覆盖了大多数典型的用例。这名单已经包括了一些熟悉的像的map,filter,zip,observeOn,…

这是所有的操作符列表

对于每个操作符,都有一个纹理图,有助于解释它是如何工作的。

但是,如果需要一些不在该列表中的操作符,该怎么办?那么,你可以建立自己的操作符。

如果由于某种原因创建那种操作符真的很难,或者你需要使用一些遗留的有状态代码,该怎么办?好吧,已经弄得一团糟,但可以轻松跳出Rx环境,处理数据,然后返回到它。

RxSwift文档二(为何使用Rx)相关推荐

  1. Django框架深入了解_05 (Django中的缓存、Django解决跨域流程(非简单请求,简单请求)、自动生成接口文档)(二)

    二.跨域: 回到顶部 跨域知识介绍: 点我以前博客 跨域解决方法:CORS:跨域资源共享 CORS请求分类(简单请求和非简单请求) 简单请求(simple request):只需要在头信息之中增加一个 ...

  2. C# VS2012操作word文档 (二).插入表格图片

    在上一篇文章"C# VS2012创建word文档.(一)"中我们讲述了如何使用VS2012引用COM中Miscrosoft Word 14.0 Object Library实现创建 ...

  3. SpringCloud从入门到精通(超详细文档二)

    上一篇文档(SpringCloud从入门到精通之超详细文档一)已经对Springboot/SpringCloud做了简单的介绍以及应用讲解,下面将继续为大家介绍SpringCloud后续应用. 第12 ...

  4. Butterfly主题安装文档(二)之主题配置

    语言 修改项目配置文件 _config.yml 默认语言是 en language: zh-CN 主题支持三种语言: default(en) zh-CN (简体中文) zh-TW (繁体中文) 网站资 ...

  5. (马世龙)Linux下CACTI完全搭建技术文档二

    续(马世龙)Linux下CACTI完全搭建技术文档一 6.完成cacti的安装 1. 首先检查一下rra/下面,有没有数据 2. snmpwalk -v 2c -c public ServerIP i ...

  6. 翻译qmake文档(二) Getting Started

    翻译qmake文档 目录 原英文文档: http://qt-project.org/doc/qt-5/qmake-tutorial.html 本教程教讲授qmake基础知识.这个手册里的其它专题包含更 ...

  7. Xadmin文档(二)

    转载自:https://xadmin.readthedocs.org/en/latest/views_api.html Views class xadmin.views. BaseAdminObjec ...

  8. Java生成doc文档二(做一个简单的封面)

    在上一篇博客中,我们提到了三个重要的概念,下面来就来实际做一个word文档的封面. 我们创建的是Maven项目,添加两个Maven依赖如下: <dependencies><depen ...

  9. java long 除法运算_java基础知识学习文档二

    一.基本概念 1 对象: 对象是Java类的一个实例,它有状态.属性.行为.如:一个人,具体的某一个人是人类的一个实例.这个人他有名字.性别.年龄等属性,他可以吃饭.说话.走路等行为. 2 类: 类是 ...

最新文章

  1. 语义分割-- SegNet:A Deep Convolutional Encoder-Decoder Architecture for Image Segmentation
  2. 两个数据库字符集不一样,如何快速增量同步数据.
  3. 【转】(原創) 如何使用ModelSim-Altera對Nios II仿真? (SOC) (Nios II) (SOPC Builder) (ModelSim) (DE2)...
  4. python中元祖 字典 列表的区别_Python中元祖,列表,字典的区别
  5. 微信小程序云开发分页刷新获取数据
  6. 和平精英有电脑版吗_群雄逐鹿丨攀升电脑见证NEST和平精英王者诞生!
  7. GitHub官网入门教程翻译
  8. JavaScript + jQuery 知识复习总结(附超实用jQuery中文文档)
  9. 两个栈实现一个队列(图解),一看就懂
  10. python爬虫框架教程_python爬虫框架有哪些
  11. 链栈(入栈,出栈,遍历)
  12. RTI_DDS使用参与者QoS属性编辑传输TCP
  13. eps在c语言,C语言中eps指的是什么东西?
  14. 激情巴布部落畅快一日游
  15. c语言中puts的作用是,C语言的字符串输出puts()函数
  16. 无人驾驶入门——2D检测 基于图片的检测算法(四)
  17. speedoffice表格如何重复打印标题?
  18. iphone7 买不买???
  19. 工控网关linux用什么芯片,重庆工控嵌入式主板原理,工业网关怎么挑
  20. javascript语言,定义变量

热门文章

  1. 什么时候买保险最好?
  2. Protobuf 学习手册——语法篇
  3. 最全整理!37 个 Python Web 开发框架总结
  4. 我心中的坏男孩,愿你健康成长,早日闯出属于你自己的天地
  5. FLV Video解析
  6. Python基础经典问题-天天向上的力量
  7. 第11期《codeforces 1167A - Telephone Number 题解 》
  8. Salesforce公司简介
  9. 道生一,一生二,二生三,三生万物
  10. 在中国,程序员能干一辈子吗?