ReactiveCocoa简介翻译

> 作为一个iOS开发小鱼,一直对RAC的使用垂涎不已,却一直没能深入学习,在项目闲暇的空档自娱自乐做个官方简介的中文翻译(结合google翻译).能力有限,如有谬误,还望指正海涵.以下为正文

ReactiveCocoa

框架代码地址:[https://github.com/ReactiveCocoa/ReactiveCocoa][1]

ReactiveCocoa(RAC)是一个Cocoa框架,灵感来自于函数响应式编程。它提供了实时地对数据流steams of values变化进行展示和响应的API。

目录:

  1. 介绍

  2. 示例:即时网络搜索

  3. Objective-C和Swift的支持情况

  4. RAC与Rx之间的关系

  5. 使用方法

  6. 操作步骤

如果你已经熟悉了函数响应式编程或基本了解了ReactiveCocoa,可以去文档文件夹里了解它的运行原理等信息。或者,直接进入我们的文档评论区,了解更多的相关API。

如果您有什么疑问,请先查看该问题在问题讨论区StackOverflow上是否已经有了相关的讨论.如果没有的话,可随时提交给我们!

兼容性

这份RAC4文档适用于Swift 2.2.x. 关于Swift 1.2的支持请看RAC3.

介绍

ReactiveCocoa的灵感来自于函数响应式编程.

不同于就地即时地替换和修改可变变量的方案,RAC提供了“事件流”方案,通过信号Signal和信号产生器的SignalProducer形式来展示和响应实时的数据值values的变化.

"事件流"统一了Cocoa里所有的非即时的事件处理模式,包括了:

  • 代理方法 Delegate methods

  • block回调 Callback blocks

  • 通知模式 NSNotifications

  • 控制动作和响应链事件 Control actions and responder chain events

  • 观察者模式 Key-value observing (KVO)

  • Futures and promises (不了解)

由于所有这些不同的机制可以通过相同的方式展示,容易发现将它们整合链接在一起,可以使代码更精简高效,同时使项目统一性更高.less spaghetti code and state to bridge the gap

更多的有关ReactiveCocoa概念信息,请参见框架概述

实例:在线搜索,即时响应

比方说,你有一个文本输入框,当用户键入字段,你想即时对其进行搜索查询。

监控文本编辑

第一步是监控文本输入框的字段编辑情况,专门对UITextField使用RAC扩展以实现这个需求:

let searchStrings = textField.rac_textSignal()
    .toSignalProducer()
    .map { text in text as! String }

以上代码给我们提供了一个能够发送字符串类型的值的信号产生器. (从Objective-C桥接扩展方法是相当必要的).

建立网络请求

我们需要随着字符串改变而同时执行网络请求. 同时,RAC提供的一个叫做NSURLSession的扩展可以满足这个需求:

let searchResults = searchStrings
    .flatMap(.Latest) { (query: String) -> SignalProducer<(NSData,NSURLResponse),NSError> in
        let URLRequest = self.searchRequestWithEscapedQuery(query)
        return NSURLSession.sharedSession().rac_dataWithRequest(URLRequest)
    }
    .map { (data,URLResponse) -> String in
        let string = String(data: data,encoding: NSUTF8StringEncoding)!
        return self.parseJSONResultsFromString(string)
    }
    .observeOn(UIScheduler())

这个将在主线程上将我们的字符串产生器转换成一个包含搜索结果的数组的产生器.(感谢UIScheduler).
此外,flatMap(.Latest) 确保了只有最后一个搜索操作能被执行.

如果用户在执行网络请求时候输入了其他的字节,那么该网络请求将在下一个网络请求开始前被取消.

试想如果要自己写,需要多少代码才能实现这个效果.

接收结果

这不会马上真正地执行,因为如果要收取搜索结果,那么信号产生器必须先运行(这样可以防止无效运行).这很容易做到:

searchResults.startWithNext { results in
    print("Search results: \(results)")
}

这样,我们要做的就是等待包含搜索结果的事件,然后将事件里的搜索结果打印到控制台,而这个打印操作可以很简单地用其他操作替代,比如说刷新屏幕上显示的各种视图.

故障处理

这个例子写到这一步,随便一个网络故障都有可能终止事件流.甚至可能会导致所有未来的查询操作都不会进行.

对此,我们需要决定当故障发生时应该如何处理.最快的解决办法就是先记录故障,然后忽略他们.

.flatMap(.Latest) { (query: String) -> SignalProducer<(NSData,NSError> in
    let URLRequest = self.searchRequestWithEscapedQuery(query)

    return NSURLSession.sharedSession()
       .rac_dataWithRequest(URLRequest)
       .flatMapError { error in
          print("Network error occurred: \(error)")
          return SignalProducer.empty
        }
    }

我们可以用一个很有效的故障忽略办法,就是:通过用空事件流替换故障.

但在放弃之前多做几次尝试会更合适一些,而且有一个很方便的重试操作正对应这个需求.

我们的改进后的搜索结果产生器可以是这样的:

let searchResults = searchStrings
    .flatMap(.Latest) { (query: String) -> SignalProducer<(NSData,NSError> in
        let URLRequest = self.searchRequestWithEscapedQuery(query)

        return NSURLSession.sharedSession()
            .rac_dataWithRequest(URLRequest)
            .retry(2)
            .flatMapError { error in
                print("Network error occurred: \(error)")
                return SignalProducer.empty
            }
    }
    .map { (data,encoding: NSUTF8StringEncoding)!
        return self.parseJSONResultsFromString(string)
    }
    .observeOn(UIScheduler())

减少网络请求的流量消耗

可以通过定期地执行实际的搜索操作,以减少流量.

ReactiveCocoa里有一个我们能应用于搜索的操作:节流器throttle:

let searchStrings = textField.rac_textSignal()
    .toSignalProducer()
    .map { text in text as! String }
    .throttle(0.5,onScheduler: QueueScheduler.mainQueueScheduler)

这可以防止程序发送时间间隔少于0.5秒的数据请求.

如果要自己实现这个效果将会需要签名验证的状态significant state,而且代码也会更加难以阅读! 但通过ReactiveCocoa,我们只需要导入一个时间到我们的事件流里.

调试事件流

由于其本身的特性,一个流的堆栈可能有大量的构架,其中大多通常可以使调试成为一件令人难搞的事情. 如下的插入附加作用side effect到流是一个比较基础的调试方法:

let searchString = textField.rac_textSignal()
    .toSignalProducer()
    .map { text in text as! String }
    .throttle(0.5,onScheduler: QueueScheduler.mainQueueScheduler)
    .on(event: { print ($0) }) // the side effect

而以下操作将打印出这个流的事件,同时不影响这个流的原本行为.信号产生器SignalProducer和信号*Signal都会自动为你运行打印操作:

let searchString = textField.rac_textSignal()
    .toSignalProducer()
    .map { text in text as! String }
    .throttle(0.5,onScheduler: QueueScheduler.mainQueueScheduler)
    .logEvents()

更多信息和进阶操作,请查看调试技术文档.

Objective-C 和 Swift

虽然ReactiveCocoa最开始是一个基于Objective-C的框架,但是从3.0版本开始,所有的主要功能开发都集中在Swift的API上.

在RAC里,Objective-C的API和Swift的API是完全分开的,但是二者的转换是可以桥接的.这主要是为了兼容老的旧的ReactiveCocoa项目,或者是使用了还没添加到Swift API 的Cocoa扩展的项目.

目前来说,Objective-C的API将继续存在并且得到支持,但不会得到很多的改进. 更多有关于API使用的信息,请查询我们的详细文档.

我们强烈建议所有新项目使用Swift API.

RAC与Rx之间的关系?

未完待续

ReactiveCocoa was originally inspired,and therefore heavily influenced,by Microsoft's Reactive Extensions (Rx) library.There are many ports of Rx,including RxSwift,but ReactiveCocoa is intentionally not a direct port.

Where RAC differs from Rx,it is usually to:

  • Create a simpler API

  • Address common sources of confusion

  • More closely match Cocoa conventions

The following are some of the concrete differences,along with their rationales.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom