ReactiveCocoa 4 如何使用冷热信号

ObjectiveCBridging

  • RACSignal -> Signal, SignalProducer

  • RACCommand -> Action

  • RACScheduler -> SchedulerType

  • RACDisposable -> Disposable

具体可以参看ObjectiveCBridging.swift

什么是ReactiveCocoa

什么是信号

ReactiveCocoa的冷热信号

RAC4的 Signal 对应RAC2的 RACSubject,为热信号,SignalProducer 对应 RACSignal 为冷信号。

  • 热信号是主动的,就算没有订阅者也会即刻推送;冷信号是被动的,有订阅者才推送

  • 热信号可以有多个订阅者,一对多;冷信号只能一对一,当有新的订阅者,信号是重新完整发送的

形象的说:热信号像是直播,冷信号像是点播

热信号

参看一个例子

// 创建信号
let (signal, observer) = Signal.pipe()
// 监听器1:立即监听
signal.observeResult {
    NSLog("Subscriber 1 get a next value: \($0)")
}
// 监听器2:0.1s后监听
QueueScheduler.mainQueueScheduler.scheduleAfter(NSDate(timeIntervalSinceNow: 0.1)) {
    signal.observeResult({ (result) in
        NSLog("Subscriber 2 get a next value: \(result)")
    })
}
// 发送Next信号:1s后发送"package 1"
QueueScheduler.mainQueueScheduler.scheduleAfter(NSDate(timeIntervalSinceNow: 1)) { 
    NSLog("Signal send package 1 ...")
    observer.sendNext("package 1")
}
// 监听器3:1.1s后监听
QueueScheduler.mainQueueScheduler.scheduleAfter(NSDate(timeIntervalSinceNow: 1.1)) { 
    signal.observeResult {
        NSLog("Subscriber 3 get a next value: \($0)")
    }
}
// 发送Next信号: 2s后发送"package 2"
QueueScheduler.mainQueueScheduler.scheduleAfter(NSDate(timeIntervalSinceNow: 2)) { 
    NSLog("signal send package 2 ...")
    observer.sendNext("package 2")
}

运行:

2016-12-18 19:42:16.206588 TestRAC4[695:389286] start ...
2016-12-18 19:42:17.249072 TestRAC4[695:389286] Signal send package 1 ...
2016-12-18 19:42:17.254583 TestRAC4[695:389286] Subscriber 1 get a next value: .Success(package 1)
2016-12-18 19:42:17.254899 TestRAC4[695:389286] Subscriber 2 get a next value: .Success(package 1)
2016-12-18 19:42:18.393983 TestRAC4[695:389286] signal send package 2 ...
2016-12-18 19:42:18.394386 TestRAC4[695:389286] Subscriber 1 get a next value: .Success(package 2)
2016-12-18 19:42:18.394582 TestRAC4[695:389286] Subscriber 2 get a next value: .Success(package 2)
2016-12-18 19:42:18.394769 TestRAC4[695:389286] Subscriber 3 get a next value: .Success(package 2)

正如之前提到的热信号的特点,热信号不关心是否有人监听,有产生信号就发送,有人监听就发个监听者。0.1s,2s分别发送next事件,0s,0.1s,1.1s分别产生一个监听者,所以第一个信号只有监听者1,2能收到;第二个next发出时三个监听器都准备好了,都能收到。

冷信号

再看看冷信号的例子:

let producer = SignalProducer.init { (observer, _) in
    QueueScheduler.mainQueueScheduler.scheduleAfter(NSDate(timeIntervalSinceNow: 1)) {
        NSLog("Producer send package 1 ...")
        observer.sendNext("package 1")
    }
    QueueScheduler.mainQueueScheduler.scheduleAfter(NSDate(timeIntervalSinceNow: 2)) {
        NSLog("Producer send package 2 ...")
        observer.sendNext("package 2")
    }
}
QueueScheduler.mainQueueScheduler.scheduleAfter(NSDate(timeIntervalSinceNow: 2)) {
    NSLog("setup Subsrciber 1")
    producer.startWithResult({ (msg) in
        NSLog("Subscriber 1 get a next value: \(msg)")
    })
}
QueueScheduler.mainQueueScheduler.scheduleAfter(NSDate(timeIntervalSinceNow: 3)) {
    NSLog("setup Subsrciber 2")
    producer.startWithResult({ (msg) in
        NSLog("Subscriber 2 get a next value: \(msg)")
    })
}

以下是输出:

2016-12-18 20:05:36.396118 TestRAC4[703:394073] start ...
2016-12-18 20:05:38.600637 TestRAC4[703:394073] setup Subsrciber 1
2016-12-18 20:05:39.681697 TestRAC4[703:394073] setup Subsrciber 2
2016-12-18 20:05:39.682077 TestRAC4[703:394073] Producer send package 1 ...
2016-12-18 20:05:39.685559 TestRAC4[703:394073] Subscriber 1 get a next value: .Success(package 1)
2016-12-18 20:05:40.753463 TestRAC4[703:394073] Producer send package 2 ...
2016-12-18 20:05:40.753865 TestRAC4[703:394073] Subscriber 1 get a next value: .Success(package 2)
2016-12-18 20:05:40.754679 TestRAC4[703:394073] Producer send package 1 ...
2016-12-18 20:05:40.754898 TestRAC4[703:394073] Subscriber 2 get a next value: .Success(package 1)
2016-12-18 20:05:41.881230 TestRAC4[703:394073] Producer send package 2 ...
2016-12-18 20:05:41.881673 TestRAC4[703:394073] Subscriber 2 get a next value: .Success(package 2)

使用冷信号,信号在程序起来第1s,2s分别发送了一个next。而在2s,3s分别建立了一个监听。第一个监听建立后1s,2s分别收到一个信号。第二信号建立的1s,2s后也分别收到了同样的信号。

冷信号转化为特殊热信号

由于冷信号每次订阅都重新发送事件,故有时候我们需要多次订阅时会造成不希望的重复发送(这称为信号的副作用),我们需要将冷信号转为具有热信号特征的冷信号。

参看一个例子,我们用冷,热信号在0.1s后发送三个next事件,然后如下进行监听:

// 我们用2个监控来监控generateSignal()产生的信号
let signal = generateSignal()

signal.startWithResult{ NSLog("Subscriber 1 get a next value: \($0)") }

QueueScheduler.mainQueueScheduler.scheduleAfter(NSDate(timeIntervalSinceNow: 1)) {
    signal.startWithResult{ NSLog("Subscriber 2 get a next value: \($0)") }
}

先看看如果是产生了普通的冷信号:

func generateSignal() -> SignalProducer {
    let producer = SignalProducer.init { (observer, _) in
        QueueScheduler.mainQueueScheduler.scheduleAfter(NSDate(timeIntervalSinceNow: 1)) {
            NSLog("producer send package 1 ...")
            observer.sendNext("package 1")
            NSLog("producer send package 2 ...")
            observer.sendNext("package 2")
            NSLog("producer send package 3 ...")
            observer.sendNext("package 3")
        }
    }
    return producer
}

输出如我们所料,信号被重复发送了。

2016-12-18 21:46:21.295670 TestRAC4[742:413995] start ...
2016-12-18 21:46:22.389634 TestRAC4[742:413995] producer send package 1 ...
2016-12-18 21:46:22.397032 TestRAC4[742:413995] Subscriber 1 get a next value: .Success(package 1)
2016-12-18 21:46:22.397397 TestRAC4[742:413995] producer send package 2 ...
2016-12-18 21:46:22.398235 TestRAC4[742:413995] Subscriber 1 get a next value: .Success(package 2)
2016-12-18 21:46:22.398415 TestRAC4[742:413995] producer send package 3 ...
2016-12-18 21:46:22.398608 TestRAC4[742:413995] Subscriber 1 get a next value: .Success(package 3)
2016-12-18 21:46:23.496484 TestRAC4[742:413995] producer send package 1 ...
2016-12-18 21:46:23.496886 TestRAC4[742:413995] Subscriber 2 get a next value: .Success(package 1)
2016-12-18 21:46:23.497061 TestRAC4[742:413995] producer send package 2 ...
2016-12-18 21:46:23.497255 TestRAC4[742:413995] Subscriber 2 get a next value: .Success(package 2)
2016-12-18 21:46:23.497413 TestRAC4[742:413995] producer send package 3 ...
2016-12-18 21:46:23.498869 TestRAC4[742:413995] Subscriber 2 get a next value: .Success(package 3)

如果改成热信号呢?

func generateSignal() -> Signal {
    let (signal, observer) = Signal.pipe()
    QueueScheduler.mainQueueScheduler.scheduleAfter(NSDate(timeIntervalSinceNow: 0.1)) {
        NSLog("producer send package 1 ...")
        observer.sendNext("package 1")
        NSLog("producer send package 2 ...")
        observer.sendNext("package 2")
        NSLog("producer send package 3 ...")
        observer.sendNext("package 3")
    }
    return signal
}

let signal = generateSignal()

signal.observeResult { NSLog("Subscriber 1 get a next value: \($0)") }

QueueScheduler.mainQueueScheduler.scheduleAfter(NSDate(timeIntervalSinceNow: 1)) {
    signal.observeResult{ NSLog("Subscriber 2 get a next value: \($0)") }
}

输出也如我们所料,只有监听2能搜到事件,且信号都只发送一次。

2016-12-18 21:54:34.527911 TestRAC4[745:415500] start ...
2016-12-18 21:54:34.634711 TestRAC4[745:415500] producer send package 1 ...
2016-12-18 21:54:34.637899 TestRAC4[745:415500] Subscriber 1 get a next value: .Success(package 1)
2016-12-18 21:54:34.638067 TestRAC4[745:415500] producer send package 2 ...
2016-12-18 21:54:34.638146 TestRAC4[745:415500] Subscriber 1 get a next value: .Success(package 2)
2016-12-18 21:54:34.638195 TestRAC4[745:415500] producer send package 3 ...
2016-12-18 21:54:34.638334 TestRAC4[745:415500] Subscriber 1 get a next value: .Success(package 3)

缓冲 buffer

通过SignalProducer.buffer()创建,是一个事件的队列(通常指定数量),当新信号产生时,会重新执行队列里的事件。和pipe相似,这个方法返回一个观察者。每个发给这个观察者的事件会被加入队列。如果这个缓冲区已经达到创建时预定的数量,当新的事件发来时,最早的一个会被移出队列。

如果使用buffer来处理呢?我们将上面的冷信号稍作修改:

func generateSignal() -> SignalProducer {
    let (producer, observer) = SignalProducer.buffer(Int.max)
    QueueScheduler.mainQueueScheduler.scheduleAfter(NSDate(timeIntervalSinceNow: 1)) {
        NSLog("producer send package 1 ...")
        observer.sendNext("package 1")
        NSLog("producer send package 2 ...")
        observer.sendNext("package 2")
        NSLog("producer send package 3 ...")
        observer.sendNext("package 3")
    }
    return producer
}

输出发现,似乎冷信号变成了热信号了,只发送了一次。但又具有冷信号的特点:两个监听器都能收到事件。

2016-12-18 21:44:29.971493 TestRAC4[740:413482] start ...
2016-12-18 21:44:31.071352 TestRAC4[740:413482] producer send package 1 ...
2016-12-18 21:44:31.075839 TestRAC4[740:413482] Subscriber 1 get a next value: .Success(package 1)
2016-12-18 21:44:31.076115 TestRAC4[740:413482] producer send package 2 ...
2016-12-18 21:44:31.076580 TestRAC4[740:413482] Subscriber 1 get a next value: .Success(package 2)
2016-12-18 21:44:31.076773 TestRAC4[740:413482] producer send package 3 ...
2016-12-18 21:44:31.077035 TestRAC4[740:413482] Subscriber 1 get a next value: .Success(package 3)
2016-12-18 21:44:31.078167 TestRAC4[740:413482] Subscriber 2 get a next value: .Success(package 1)
2016-12-18 21:44:31.078420 TestRAC4[740:413482] Subscriber 2 get a next value: .Success(package 2)
2016-12-18 21:44:31.078771 TestRAC4[740:413482] Subscriber 2 get a next value: .Success(package 3)

buffer(Int.max)这里的参数表示这个缓冲大小,如果设置为1,那么第二个next发出时,缓存里第一个就被替换成了第二个next,第三个来时同理。对于第一个监听者它能收到全部事件;对于第二个监听者只能收到缓存里的事件也就是最后的package 3。如果这第一个监听者在发出信号之后再开始监听,那么它也将和第二个监听者一样只能收到缓冲里的事件。

replayLazily

假如我们不能修generateSignal()里的代码,这是由第三方提供的,我们可以使用replayLazily。

let producer = generateSignal().replayLazily(Int.max)  // 直接用之前的冷信号

producer.startWithResult { NSLog("Subscriber 1 get a next value: \($0)") }

QueueScheduler.mainQueueScheduler.scheduleAfter(NSDate(timeIntervalSinceNow: 1)) {
    producer.startWithResult { NSLog("subscriber 2 get a next value: \($0)") }
}

输出如下,和buffer是一样的效果:

2016-12-18 22:04:48.364076 TestRAC4[753:417953] start ...
2016-12-18 22:04:48.482124 TestRAC4[753:417953] producer send package 1 ...
2016-12-18 22:04:48.484271 TestRAC4[753:417953] Subscriber 1 get a next value: .Success(package 1)
2016-12-18 22:04:48.484422 TestRAC4[753:417953] producer send package 2 ...
2016-12-18 22:04:48.484665 TestRAC4[753:417953] Subscriber 1 get a next value: .Success(package 2)
2016-12-18 22:04:48.484852 TestRAC4[753:417953] producer send package 3 ...
2016-12-18 22:04:48.485120 TestRAC4[753:417953] Subscriber 1 get a next value: .Success(package 3)
2016-12-18 22:04:49.454274 TestRAC4[753:417953] subscriber 2 get a next value: .Success(package 1)
2016-12-18 22:04:49.454659 TestRAC4[753:417953] subscriber 2 get a next value: .Success(package 2)
2016-12-18 22:04:49.454897 TestRAC4[753:417953] subscriber 2 get a next value: .Success(package 3)

那么replayLazily(Int.max)这个参数和buffer的相同吗?我们修改成1或2一看就知道。答案是相同

你可能感兴趣的:(ios,swift,reactivecocoa)