ReactiveCocoa学习笔记三--框架总览(译)

惯例废话

学习了一段时间的RAC后, 对有些概念开始慢慢清晰, 然后再次看了这篇文档, 发现的确是高屋建瓴地指出了很多基本的概念, 这篇笔记我将以翻译为主, 注解为辅来写, 同时会尽我所能来解释一些名词.

文末对这篇文档的主要内容做了一次简单的概括, 嫌字多可以自己跳到最后查看

(o(╯□╰)o, 简书貌似不支持页面内标签跳转, 关于附录的东西,各位看官要手动拖到最下面了)

原文中的一些名词我打算保留, 避免在描述和类名之间对应加上一道鸿沟, 下面是一些参考释意:

名词 释意
Stream
Monad 单子
Signal 信号
Subscribe(r) 订阅(者)
Dispose 解除
Completed 完成
Subject 主题
Command 指令
Connection 连接
scheduler 调度器

框架总览

这篇文档包含了对ReactiveCocoa(译注:后文简称RAC)框架中各个组件的简要叙述, 以及对这些组件各自的职责和如何协同工作进行解释. 也就是说, 这篇文档是学习新模块的起点, 从这里去找对应的文档来看.

例如, 关于RAC的例子和帮助文档, 查看README或者设计指导 (译注: 这篇文档也很有帮助, 值得一看)

Streams

Stream, 也就是RACStream抽象类, 是一系列对象的值.

值可能即可可用, 也可能将来可用(译注: 例如懒加载), 但是必须按序列来取. 没有办法绕过计算第一个值而直接获取后续的值.

Stream是Monads(译注: 关于Monad我在附录中有解释, 看看先). 相比于其它非Monad的东西, Monad可以通过几个简单基本的操作来构建复杂的操作. RACStream的实现类似于Haskell的Monoid和MonadZip.

RACStream本身并没有什么用, 大多数stream都被当做signalssequences了.

Signals

signal, 也就是RACSignal类, 是一个push驱动的stream(译注: 所谓push驱动和pull驱动是数据源的角度来看的, 对应为主动和被动, push是数据源会把数据主动推出去, pull则是要自己去拉取才会拿到数据).

Signals通常代表将来会被取出的数据. 当代码执行完毕或数据已接收, 信号的值已被发送给subscriber. 用户必须订阅一个signal才能访问它的值.

signals发送给subcriber三种不同类型的事件:

  • next事件提供了一个stream的新值. RACStream的方法只操作这种事件类型. 与Cocoa的集合不同的是, 信号包含nil是允许的.
  • error事件意味着在信号结束之前有错误发生了. 这个事件可能包含了一个NSError对象来指示错误. 错误必须被特殊处理掉, 它们也不会包含在stream的值中.
  • completed事件意味着信号已经成功结束, 并且没有更多的值会被加入到stream中了. completed事件必须要被特殊处理, 它不会被stream包含.

一个信号的生命周期中包含了一系列的next事件, 最终伴随一个error或者completed事件而结束(不会2者同时出现).

Subscription

一个Subscriber可以是任何东西, 它等待着信号的事件到来. 在RAC中, subscriber是任意实现了RACSubscriber协议的对象.

通过调用-subscribeNext:error:completed:方法或其他便利方法来创建一个subscription. 技术上来说, 大多数RACStream和RACSignal操作也会创建subscription, 通常是一些实现细节的中间态subscription.

Subscription会持有信号, 但是会在signal发送completed或者error事件后自动dispose.(译注: RAC内存管理第一条--必须保证signal以completed或error事件收尾). subscription也可以手动dispose

Subjects

subject, 也就是RACSubject类, 是一个可以手动控制的Signal.(译注: 也就是实现了RACSubscription协议)

Subject可以被看作是"可变"的signal, 就像NSMutableArray之于NSArray. Subject在把非RAC代码代入到Signal的世界中起到了非常重要的桥接作用.

例如, 可以取代在block回调中处理应用逻辑, 这些block可以简单地发送事件来共享subject. subject可以作为RACSignal返回, 隐藏回调的实现细节.

一些Subject还提供了额外的操作. 比如RACReplaySubject可以用来缓存事件, 分发给以后的subscriber, 就像一个网络请求已经结束了, 但是其它处理这个结果的事情还未就绪.

(译注: Subject相比较Signal有三个需要注意的点, 一是Subject可以自己发送事件, 而Signal不行; 二是Subject发送的值是共享的, 无论多少subcriber收到的都是同一份, 而Signal不是; 三是Subject是热信号(Hot Signal)而Signal是冷信号(Cold Signal). 关于冷热信号在附录也有简单阐述, 看看先)

Commands

Command, 也就是RACCommand类, 创建和订阅一个Signal来响应一些操作. 这使用户在APP上的交互当作副作用(side-effecting)(译注: side-effecting在学习RAC中会出现多次, 总体来说就是用户对状态的修改, 因为RAC的本意就是封装了所有状态的修改, 因此, 如果用户自己在某些代码中要自己管理某些状态, 就会导致和一般字面上的意义不一样, 就是副作用的代码, 具体可以看看学习笔记四中使副作用显性化的描述.)来执行变得更加容易了.

通常一个触发指令的作用是UI驱动的, 例如一个按钮被点击了. Command也可以通过信号来自动disable掉, 并且这个disable的状态还可以通过UI中disable相关联的控件来体现.

在OS X上, RAC添加了一个rac_command属性到NSButton来自动设置这些行为.

Connections

connection, 也就是RACMulticastConnection类, 是一次多个subscriber之间共享的订阅.

Signal默认是cold, 意思是每一次添加了新的subscriber, 都会开始执行任务. 这个行为通常是值得的, 因为这意味着对每个subscriber来说, 数据每次都会被重新计算, 但是这样的行为在Signal有副作用或任务庞大的时候也会出问题(例如, 发送一个网络请求).

connection通过RACSignal的-publish-multicast方法来创建, 并且保证无论有这个connection被订阅了多少次, 都只有一个潜在的订阅被创建. 一旦连接上, connection就称被为hot, 并且潜在的订阅会一直保持活跃, 直到所有的的connection的订阅都已经dispose.

Sequences

sequences, 也就是RACSequence类, 是一个pull驱动的stream.

Sequence算是一种集合类, 类似于NSArray. 不过与数组不同的是, sequence里面的值默认是计算的(只有当它们被用到时才计算), 这样提高了只有一部分sequence值被访问的性能. 与Cocoa集合类一样, sequence不能包含nil.

Sequence与Clojure的sequence或Haskell的List类似.

RAC添加了-rac_sequence方法到大多数Cocoa的集合类中, 从而允许它们使用RACSequence来替代之.

Disposables

RACDisposable类被用来取消操作和资源清理.

Disposables最常用来解除对Signal的订阅. 当订阅被dispose, 对应的subscriber将再也不会受到Signal发送的事件了. 除此之外, 任何与此次订阅相关的任务(后台处理, 网络请求等)都会被取消掉, 因为这些结果已经不再需要了.

查看RAC的设计指导获取更多关于取消操作的信息.

Schedulers

scheduler, 也就是RACScheduler类, 是一系列的操作队列来支持Signal执行任务或分发结果.

scheduler与GCD队列类似, 而且scheduler支持去掉操作(因为Disposables), 并且总是顺序执行. 有一个例外就是+immediateScheduler, 这个scheduler不会提供同步执行. 这有助于防止死锁, 并且鼓励使用Signal操作符来取代阻塞任务.

RACScheduler与NSOperationQueue也有一些类似, 但是scheduler不支持任务排序或或者说是一个任务依赖另外一个.

Value types

RAC提供了一些五花八门的类来方便stream中值的表示:

  • RACTuple是一个轻量, 常量大小的集合, 允许包含nil(会被表示为RACTupleNil). 通常被来表示多个stream值的混合.
  • RACUnit是一个表示"空值"的单例. 它用来表示stream中已经没有更多有意义的值了
  • RACEvent把任意的Signal事件表示为一个单一的值. 主要用于RACSignal的-materialize方法.

<a name="Conclusion"></a>总结

这篇文档除了描述了一些名词的概念之外, 更重要的是告诉了我们各个类之间的细微差别:

  • RACSignal是冷信号, 每次订阅都会执行一次任务
  • RACSubject是热信号, 每个任务都只执行一次, 各subscriber共享结果
  • RACReplaySubject相比RACSubject多了一个缓冲区, 可以回放之前的任务
  • RACCommand主要用于UI相关的操作
  • RACDisposable可以取消对Signal的订阅
  • RACMulticastConnection可以合并多个订阅为一个, 避免任务被多次执行
  • ....

附录

<a name="Monad"></a>Monad

关于Monad在Wikipedia的解释中有一句:

A monad is defined by a return operator that creates values, and a bind operator used to link the actions in the pipeline.

也就是说, 一个Monad被定义为返回一个构造值的操作符, 并且一个bind操作符用来在管道中连接多个操作.

貌似还挺绕, 其实就是说, Monad本质是一个操作符(可以理解为一个函数), 这个操作符的作用就是返回一个数值. 为什么这么麻烦, 而不直接返回这个数值呢? 后面这句话解释了原因, 就是如果直接返回数值的话, 那就无法继续连接操作了, 所以这边定义了一个bind操作来专门连接多个操作符. 具体参考Linux命令行的管道.

<a name="ColdHotSignal"></a>冷热信号

关于冷热信号我之前在网上看到一位博主的博客关于冷热信号的解释大意如下:

Signal就像插座, subscriber就像是插头, 如果没有subscriber来订阅Signal, 那就是"没通电", 这信号就是"冷"的, 反之就是"热"的.

然后我又看到美团的技术博客上说的不一样, 主要信息摘抄图下:

1.热信号是主动的, 即使没有订阅者, 也会时刻推送...而冷信号是被动的, 只有订阅的时候才会发送信息...

2.热信号可以有多个订阅者,是一对多,信号可以与订阅者共享信息...而冷信号只能一对一,当有不同的订阅者, 消息会重新完成发送...

两位描述差距很大, 所以秉着追寻事实的心态, 我找到了官方文档里面有这么一句冷热信号对比:

Is the signal hot (already activated by the time it's returned to the caller) or cold (activated when subscribed to)

上下文是在探究RACSignal所代表的含义, 从这里我们可以看出冷热信号的一个重要区别:

热信号: 这个信号返回给调用者的时候就已经激活
冷信号: 当被订阅的时候激活

在文档中没有找到更多的对比解释, 但我觉得已经足够描述两者的区别了. 所以从这可以看出, 美团技术团队的描述是更加准确的, 当然, 关于一对多这个说法, 我暂且保留意见, 因为这不是核心区别, 冷信号也可以有多个subscriber, 只不过值不是共享的.

这也提醒我们, 很多时候官方文档还是准确的, 很多时候我们在学习的时候, 如果可以的话, 还是尽量去看原文档, 尽量减少理解误差.

还值得一提的是, RAC3.0版本对冷热信号的类使用上有一定的修改, 参考目录

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

推荐阅读更多精彩内容

  • 先贴上我看的博客,大部分内容来自以下博客 入门教程 sunnyxx的博客,共四篇 美团的四篇博客 一篇关于repl...
    Auther丶阅读 3,315评论 1 40
  • 前言 很多blog都说ReactiveCocoa好用,然后各种秀自己如何灵活运用ReactiveCocoa,但是感...
    RainyGY阅读 1,340评论 0 1
  • 1.ReactiveCocoa简介 ReactiveCocoa(简称为RAC),是由Github开源的一个应用于i...
    清蘂翅膀的技术阅读 1,988评论 0 1
  • RAC使用测试Demo下载:github.com/FuWees/WPRACTestDemo 1.ReactiveC...
    FuWees阅读 6,378评论 3 10
  • 1.ReactiveCocoa常见操作方法介绍。 1.1 ReactiveCocoa操作须知 所有的信号(RACS...
    萌芽的冬天阅读 1,023评论 0 5