ReactiveCocoa 4 官方文档翻译

我翻译的RAC4的文档
ReactiveCocoa 4 官方文档翻译
ReactiveCocoa 4 文档翻译:基本操作符(一)
ReactiveCocoa 4 文档翻译:基本操作符(二)
ReactiveCocoa 4 文档翻译:框架组成介绍
ReactiveCocoa 4 文档翻译:兼容Objective-C
ReactiveCocoa 4 文档翻译--设计指南(一):事件的规范
ReactiveCocoa 4 文档翻译:设计指南(二):信号的规范
[翻译]ReactiveCocoa 4 最佳实践


ReactiveCocoa (RAC) 是一个Cocoa框架,受Functional Reactive Programming启发。它提供Api合成变换(composing and transforming)随着时间改变的数据流

兼容性

关于 RAC 4的文档是在Swift 2.1.x下使用。对于Swift 1.2的支持请看 RAC 3.

介绍

ReactiveCocoa来源于functional reactive programming(Input and Output)
区别于使用不断变化修改的变量,RAC提供了“事件流”,通过 SignalSignalProducer 类型来表示, 它们随着时间发送值。
事件流统一了Cocoa用于事件和异步处理的常用模式,包括:

因为这些不同的机制能够用一种相同的方式处理,可以很容易的声明成链(chain)并且把它们联合在一起,用更少的代码和状态连接它们。
更多关于RAC里的概念可以查看Framework Overview.

例子:在线搜索

假设你有一个text field,每当用户输入文字时,你都会发起一个网络请求根据输入的关键字查询。

实时观察文字变化

实现这个目的的第一步,使用RAC中UITextField的一个扩展方法:
<pre><code>
let searchStrings = textField.rac_textSignal()

.toSignalProducer()

.map { text in text as! String }
</code></pre>
这样产生了一个signal producer 来发送输入的字符串。 ( text as! String这种类型映射在当前是必需的,为了从OC桥接当前的扩展方法

发送网络请求

获得了每次输入的字符串,我们想要发送一个网络请求。RAC也为我们提供了NSURLSession的一个扩展方法来实现:
<pre><code> 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())
</code></pre>
这段代码将字符串的producer转换成了一组包含了搜索结果的producer,并且会在主线程中处理(由于UIScheduler的作用).。
另外这里的flatMap(.Latest)确保只有最后的一次搜索会被执行。
如果当一个请求还在处理中,此时用户输入新的字符,在发起新的请求之前会取消之前的请求。想想这些如果自己实现的需要写多少代码!

接收结果

这样实际上还没有执行,因为producers必须started后才执行(对于返回结果没有被使用时做的优化)。这个很简单:
<pre><code>searchResults.startWithNext {

results in print("Search results: (results)")

}
</code></pre>
我们获取值用于<code>Next</code>中的event:包含了请求结果,并且把他们log到了控制台。这里也可以写其他UI控制的代码,比如reload一个table view。

失败处理

到目前为止,这个例子中,任何的网络错误都会产生一个<code>Failed</code> event,这会导致整个事件流终止。不幸的是,这意味着之后请求不会被发起。
为了避免这样的情况,我们需要决定当失败发生时怎样处理。最快速的解决方案就是记录它们,然后忽略它们:
<pre><code>.flatMap(.Latest) { (query: String) -> SignalProducer<(NSData, NSURLResponse), NSError> in

let URLRequest = self.searchRequestWithEscapedQuery(query)

return NSURLSession.sharedSession()

.rac_dataWithRequest(URLRequest)

.flatMapError { error in

print("Network error occurred: (error)")

return SignalProducer.empty

}

}
</code></pre>
通过将失败替换为空事件流,可以有效的忽略它们。
然而,在放弃前最好重试几次。在一次,使用retry可以方便的达到这个目的。
改进后的searchResults producer会像这样:
<pre><code>
let searchResults = searchStrings

.flatMap(.Latest) { (query: String) -> SignalProducer<(NSData, NSURLResponse), 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, URLResponse) -> String in

let string = String(data: data, encoding: NSUTF8StringEncoding)!

return self.parseJSONResultsFromString(string) }

.observeOn(UIScheduler())
</code></pre>

降低请求频率

现在,我们假设你只希望在用户输入暂停时才发起请求以减少网络请求。
RAC通过<code>throttle</code>操作符可以用于实现这个需求:
<pre><code>
let searchStrings = textField.rac_textSignal()

.toSignalProducer()

.map { text in text as! String }

.throttle(0.5, onScheduler: QueueScheduler.mainQueueScheduler)
</code></pre>
这样当间隔小于0.5秒时值不会被发送出去,意味着用户必须停止编辑0.5秒后我们才会使用输入的字符串去搜索。
如果用自己实现这个功能,真是要写不少代码,而且还会影响代码的可读性。但是在RAC中,只需要一个操作符就可以方便将时间控制加入事件流。

Objective-C 和 Swift

虽然RAC是作为一个OC的框架开始的,但是从 版本3.0开始,所有的主要特性开发都是基于 Swift API
RAC的Objective-C API和Swift API是完全分开的,但是有一个bridge可以将他们互相转换。这主要是为了兼容使用RAC的一些老项目,或者用于一些还没有被加入Swfit API的一些Cocoa扩展。
Objective-C API将继续存在,在可预计的未来将提供支持,但是不会有新的改进。关于使用这些API的信息可以参考:legacy documentation
我们强烈建议所有的新项目采用Swift API。

ReactiveCocoa和Rx的关系

ReactiveCocoa 受了 Microsoft’s Reactive Extensions (Rx)库的不少影响。有很多对于Rx的实现,包括 RxSwift,但是ReactiveCocoa无意于成为Rx的一个实现子集。
RAC和 Rx的区别,主要有以下一些:

  • 更简洁的API
  • 解决了令人困惑的常见问题
  • 更贴近Cocoa的编程习惯

命名

在大部分Rx的版本中,流被称为Observables,对应于.NET中的<code>Enumerable</code>类型。
另外, Rx.NET大部分的操作符参考了 LINQ,而LINQ是用于操作传统关系型数据库的,比如<code> Select </code>和<code> Where </code>。
RAC则最关注于匹配Swift的命名规范, 使用 <code> map </code> 和<code> filter </code>来替换select和where。其他的一些命名来源于HaskellElm(使用“signal”的始祖)。

Signals and Signal Producers (“hot” and “cold” observables)

Rx中的“hot”, “cold”, and “warm” observables (对应RAC中的事件流)很容易让人困惑。

对于这个部分和Typed errors的区别,因为在我翻译的iOS响应式编程:ReactiveCocoa vs RxSwift 选谁好中有更详细的对比说明,这里就不翻译了。

Typed errors

处理错误时方式

UI编程

很多人不知道Rx怎么用。即使用Rx进行UI的编程很常见,它有几个特性用于一些特别的场景。
RAC从ReactiveUI借鉴了很多,包括 Actions的基础操作。
不像ReactiveUI不能直接将Rx改变的对于UI编程更友好,为了这个目的则进行了很多次的改动—即使这意味着和Rx的区别越来越大.

欢迎关注我的微博:@没故事的卓同学

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,551评论 4 58
  • 在外婆家的记忆没多少,依稀记得是三年级下学期回到自己家的。 外公一直都很疼我,家里来客人,在菜板上切肉的时候...
    小丫_丫阅读 593评论 1 1
  • “我一向对做家事十分痛恨……” “我一起床就开始洒扫庭院和做苦工,然后是采购和做饭,然后是收拾和洗涮,然后就跟见了...
    格紫英文阅读 826评论 8 5