ReactiveCocoa-Swift部分入门指南-SignalProducer
学习过ReactiCocoa(以下简称RAC)的同学一般都会使用Objective-C的部分,不过RAC3之后支持了Swift,目前RAC3.x支持的是Swift1.x系列,RAC4支持的是Swift2.x系列。今天花了一点时间学习了下Swift部分示例代码。这里做些记录。Swift是支持playground,可以使用Markdown编写文档,并且所见即所得的界面方便学习。更可以插入图片。方便阅读。
学习知识必备
默认你已经学过RAC-OC部分, Swift语言,并对Monad,functional Programming有些简单的了解,或者,如果你学习了RXSwift更好。
Start
1. git clone git@github.com:ReactiveCocoa/ReactiveCocoa.git
• 执行 script/bootstrap 脚本
• 如果你安装了 [Cartheage](https://github.com/Carthage/Carthage) 使用 carthage checkout
2. 打开 ReactiveCocoa.xcworkspace
3. 编译 Result-Mac scheme
4. 编译 ReactiveCocoa-Mac scheme
5. 在workSpace目录中打开ReactiveCocoa.playground
6. Choose View > Show Debug Area
PlaygroundUtility
先来观察一下这个里面有两个方法
public func scopedExample(exampleDescription: String, _ action: () -> Void) {
print("\n--- \(exampleDescription) ---\n")
action()
}
public enum Error: ErrorType {
case Example(String)
}
scopedExample 方便测试,并分割日志输出,Error也是为了测试方便。
SignalProducer
一个信号发生器,是SignalProducer类型的实例,它可以创建信号(signals)并施加附作用(side effects)。
信号发生器用来表示操作或者任务,比如网络请求,每一次对它调用start()将会生成一个新的潜在操作,并允许调用者观察它的结果。还有一个startWithSignal()方法,会给出产生的信号,允许在必要的情况下被监听多次。
根据start()方法的动作方式,被同一个信号发生器生成的信号可能会有不同的事件顺序或版本,甚至事件流完全不一样!和普通的信号不同,在观察者连接上之前,信号发生器不会开始工作(也就没有事件会生成),并且在每一个新的监听器连接上时其工作都会重新开始一个单独的工作流
。
启动一个信号发生器会返回一个销毁器(disposable,我也不知道怎么翻译合适),它可用来打断或取消被生成信号的工作。
和信号一样,信号生成器可以通过map、filter等原函数操作。使用lift方法,所有信号的原函数可以被提升成为以信号生成器为对象的操作。除此以外,还有一些用来控制何时与如何启动信号生成器的原函数,比如times。
补充,这段我是参考自ReactiveCocoa 4 图解之六——信号发生器(SignalProducer),不过我觉得说的相当之晦涩,在学习这部份的时候,我已经学习了,RAC-OC部分,RXSwift,冷热信号等概念。突然出了一个SignalProducer的概念会让读者很难理解,其实很容易就发现,SignalProducer会带来附作用(冷信号),Signal不会带来副作用(热信号),那他们就很好理解了,SignalProducer类似RACOC中的RACDynamicSignal,Signal类似RACSubject。
通过lift函数可以让热信号转变为冷信号。
如果想学习Signal部分参考文章底部链接
Subscription
一个信号生成器代表了一种可以在需要的时候才被启动的操作(不像Signal是自启动的),这种信号是冷信号,在刚开始这个信号的状态也为冷(未激活), 既然是冷信号,那么就意味这一个观察者不会错过任何被信号生成器发出的值。
补充: 像signal 是创建的时候状态为cold(理解为未激活) ,被订阅时状态为hot (理解为激活)。
但是冷信号和热信号与状态为冷热是两个不同的概念。冷信号会带来附作用,热信号不会
这里我把副作用(side effect) 理解为附作用,有时候这是一个很必要的附加操作,并不一定都是无益的,不然就没有存在的价值了。
如果读者觉得理解的不对,请指出后续会继续修改。
scopedExample("Subscription") {
let producer = SignalProducer<Int, NoError> { observer, _ in
print("New subscription, starting operation")
observer.sendNext(1)
observer.sendNext(2)
}
let subscriber1 = Observer<Int, NoError>(next: { print("Subscriber 1 received \($0)") })
let subscriber2 = Observer<Int, NoError>(next: { print("Subscriber 2 received \($0)") })
print("Subscriber 1 subscribes to producer")
producer.start(subscriber1)
print("Subscriber 2 subscribes to producer")
// Notice, how the producer will start the work again
producer.start(subscriber2)
}
--- Subscription ---
Subscriber 1 subscribes to producer
New subscription, starting operation
Subscriber 1 received 1
Subscriber 1 received 2
Subscriber 2 subscribes to producer
New subscription, starting operation
Subscriber 2 received 1
Subscriber 2 received 2
像不像是RACDynamicSignal的创建方式,这不过不同与Sinal的是 这里的发送信号的观察者是在内部通过 Signal.pipe() 生成的,不需要外部创建。
SignalProduce是冷信号,任何一个订阅者/观察者都不会错过任何事件
start方法类似与Signal的 signal.observe()方法,只不过Signal的方法只有一个作用,就是关联一个观察者,而SignalProduce的start方法还多了一个激活信号的功能。
Empty
一个会立即调用complete事件的信号生成器
/*:
### `empty`
A producer for a Signal that will immediately complete without sending
any values.
*/
scopedExample("`empty`") {
let emptyProducer = SignalProducer<Int, NoError>.empty
let observer = Observer<Int, NoError>(
failed: { _ in print("error not called") },
completed: { print("completed called") },
interrupted: { print("interrupted called") },
next: { _ in print("next not called") }
)
emptyProducer.start(observer)
}
--- `empty` ---
completed called
Signal调用的是interrup方法,暂时不知道为什么,可能是为了区分语义把。Signal是有时序的,SignalProduce是没有时序的。使用中断更合适吧。
Never
一个什么都不会发送的信号生成器
/*:
### `never`
A producer for a Signal that never sends any events to its observers.
*/
scopedExample("`never`") {
let neverProducer = SignalProducer<Int, NoError>.never
let observer = Observer<Int, NoError>(
failed: { _ in print("error not called") },
completed: { print("completed not called") },
next: { _ in print("next not called") }
)
neverProducer.start(observer)
}
--- `never` ---
buffer
创建一个事件队列可以回放已经发送的事件。
当一个值被发送的时候,它会被放进缓冲区内,如果缓冲区已经溢出,就会丢弃旧的值,
这些被缓存的值将会被保留,直到这个信号被终结。当一个信号启动的时候,如果队列里没有任何值,所有被发送的新值都会被自动转发到观察者哪里,知道观察者收到一个终止事件。
当一个终止事件被发送到队列中,观察者不会再收到任何值,并且这个事件不会被计算buffer的缓冲区大小,所以没有缓存的值都会被丢弃。
scopedExample("`buffer`") {
let (producer, observer) = SignalProducer<Int, NoError>.buffer(2)
observer.sendNext(1)
observer.sendNext(2)
observer.sendNext(3)
var values: [Int] = []
producer.start { event in
switch event {
case let .Next(value):
values.append(value)
default:
break
}
}
print(values)
observer.sendNext(4)
print(values)
let subscriber = Observer<Int,NoError>(next:{ bufferdValue in
print("\(bufferdValue)")
})
producer.start(subscriber)
}
--- `buffer` ---
[2, 3]
[2, 3, 4]
3
4
Buffer更像RACReplaySubject,所以Buffer应该也算一个热信号
startWithSignal
通过Producer返回一个Signal,当闭包调用时返会signal开始发送事件。
闭包返回一个Disposable 可以用来中断Signal或者完成。
scopedExample("`startWithSignal`") {
var started = false
var value: Int?
SignalProducer<Int, NoError>(value: 42)
.on(next: {
value = $0
})
.startWithSignal { signal, disposable in
print(signal)
print(value) // nil
}
print(value)
}
--- `startWithSignal` ---
ReactiveCocoa.Signal<Swift.Int, Result.NoError>
nil
Optional(42)
startWithNext
通过信号生成器创建一个信号,并且给这个信号内部直接构建一个观察者,在指定的闭包中会直接订阅next事件。
返回一个Disposable,可以中断这个信号。中断之后这个闭包不会再被调用
scopedExample("`startWithNext`") {
SignalProducer<Int, NoError>(value: 42)
.startWithNext { value in
print(value)
}
}
--- `startWithNext` ---
42
其实类似与OC部分的[subscribeNext: ^(id value){}],观察者不用自己构建了
注意: 这个订阅只能接受next事件
startWithCompleted
同startWithNext, 只不过只能接受complete事件
scopedExample("`startWithCompleted`") {
SignalProducer<Int, NoError>(value: 42)
.startWithCompleted {
print("completed called")
}
}
--- `startWithCompleted` ---
completed called
startWithFailed
同startWithNext, 只不过只能接受Failer事件事件
scopedExample("`startWithFailed`") {
SignalProducer<Int, NSError>(error: NSError(domain: "example", code: 42, userInfo: nil))
.startWithFailed { error in
print(error)
}
}
--- `startWithFailed` ---
Error Domain=example Code=42 "(null)"
startWithInterrupted
同startWithNext, 只不过只能接受interrupted事件事件
scopedExample("`startWithInterrupted`") {
let disposable = SignalProducer<Int, NoError>.never
.startWithInterrupted {
print("interrupted called")
}
disposable.dispose()
}
--- `startWithInterrupted` ---
interrupted called
operators
lift
这个相对难理解点。大致类似与RAC-OC部分中的bind函数,monad中的bind函数
可以理解为所有的原函数都是通过lift去实现的,接用中间信号来实现一系列的信号变换
scopedExample("`lift`") {
var counter = 0
let transform: Signal<Int, NoError> -> Signal<Int, NoError> = { signal in
counter = 42
return signal
}
SignalProducer<Int, NoError>(value: 0)
.lift(transform)
.startWithNext { _ in
print(counter)
}
}
--- `lift` ---
42
map
把每个值都转换为新的值
scopedExample("`map`") {
SignalProducer<Int, NoError>(value: 1)
.map { $0 + 41 }
.startWithNext { value in
print(value)
}
}
--- `map` ---
42
mapError
把收到的error转换为新的error
scopedExample("`mapError`") {
SignalProducer<Int, NSError>(error: NSError(domain: "mapError", code: 42, userInfo: nil))
.mapError { Error.Example($0.description) }
.startWithFailed { error in
print(error)
}
}
--- `mapError` ---
Example("Error Domain=mapError Code=42 \"(null)\"")
filter
过滤不符合条件的值
scopedExample("`filter`") {
SignalProducer<Int, NoError>(values: [ 1, 2, 3, 4 ])
.filter { $0 > 3}
.startWithNext { value in
print(value)
}
}
--- `filter` ---
4
take
take(num) 只取前几次的值
scopedExample("`take`") {
SignalProducer<Int, NoError>(values: [ 1, 2, 3, 4 ])
.take(2)
.startWithNext { value in
print(value)
}
}
--- `take` ---
1
2
observeOn
在指定的调度器上分发事件
/*:
### `observeOn`
Forwards all events onto the given scheduler, instead of whichever
scheduler they originally arrived upon.
*/
scopedExample("`observeOn`") {
let baseProducer = SignalProducer<Int, NoError>(values: [ 1, 2, 3, 4 ])
let completion = { print("is main thread? \(NSThread.currentThread().isMainThread)") }
if #available(OSX 10.10, *) {
baseProducer
.observeOn(QueueScheduler(qos: QOS_CLASS_DEFAULT, name: "test"))
.startWithCompleted(completion)
}
baseProducer
.startWithCompleted(completion)
}
--- `observeOn` ---
is main thread? true
collect
在发送完成的时候将一系列的值聚合为一个数组
scopedExample("`collect()`") {
SignalProducer<Int, NoError> { observer, disposable in
observer.sendNext(1)
observer.sendNext(2)
observer.sendNext(3)
observer.sendNext(4)
observer.sendCompleted()
}
.collect()
.startWithNext { value in
print(value)
}
}
--- `collect()` ---
[1, 2, 3, 4]
collect(count:)
在发送数据的时候(不需要发送complete)的时候将一系列的值聚合为数组,数组的长度为count,如果又很多数据,将会返回多个数组
scopedExample("`collect(count:)`") {
SignalProducer<Int, NoError> { observer, disposable in
observer.sendNext(1)
observer.sendNext(2)
observer.sendNext(3)
observer.sendNext(4)
observer.sendNext(5)
// observer.sendCompleted()
}
.collect(count: 2)
.startWithNext { value in
print(value)
}
}
--- `collect(count:)` ---
[1, 2]
[3, 4]
collect(predicate:) matching values inclusively
通过谓词将一系列的值聚合为一个数组,注意在发送complete时候,如果前面只剩下一个值,就不需要聚合,(因为没有其他元素和最后一个元素聚合),直接返回一个只有一个元素的数组。 如果没有数据则返回一个空数组。
scopedExample("`collect(predicate:)` matching values inclusively") {
SignalProducer<Int, NoError> { observer, disposable in
// observer.sendNext(1)
// observer.sendNext(2)
// observer.sendNext(3)
// observer.sendNext(4)
observer.sendCompleted()
}
.collect { values in values.reduce(0, combine: +) == 3 }
.startWithNext { value in
print(value)
}
}
--- `collect(predicate:)` matching values inclusively ---
[]
- 尝试把所有数据打开,看看什么结果,
- 尝试只注释4看什么结果
collect(predicate:) matching values exclusively
和上一个不同的是,如果谓词成功就把之前的聚合在一起。 可以理解为把成功的界限当作分隔符
scopedExample("`collect(predicate:)` matching values exclusively") {
SignalProducer<Int, NoError> { observer, disposable in
observer.sendNext(1)
observer.sendNext(2)
observer.sendNext(3)
observer.sendNext(4)
observer.sendNext(5)
observer.sendCompleted()
}
.collect { values, next in next == 3 || next == 5 }
.startWithNext { value in
print(value)
}
}
--- `collect(predicate:)` matching values exclusively ---
[1, 2]
[3, 4] // 3满足了条件所以被分开
[5] // 5也是
combineLatestWith
将第一个信号生成器的values 和被聚合信号生成器的最后一个值聚合为一个元组
。
新产生的信号生成器不会发送任何值,只是转发,任何一个原来的信号被中断,这个新的信号生成器也会中断
scopedExample("`combineLatestWith`") {
let producer1 = SignalProducer<Int, NoError>(values: [ 1, 2, 3, 4 ])
let producer2 = SignalProducer<Int, NoError>(values: [ 1, 2 ])
producer1
.combineLatestWith(producer2)
.startWithNext { value in
print("\(value)")
}
}
--- `combineLatestWith` ---
(1, 2)
(2, 2)
(3, 2)
(4, 2)
skip
skip(num), 跳过num此发送的事件
scopedExample("`skip`") {
let producer1 = SignalProducer<Int, NoError>(values: [ 1, 2, 3, 4 ])
producer1
.skip(2)
.startWithNext { value in
print(value)
}
}
--- `skip` ---
3
4
materialize
将被发送的值(value)变成Event
, 允许他们被修改。换句话说把一个值变成一个Monad (在前文中写到Event就是一个monad) 当收到一个compelet或者Failure事件,这个新的信号生成器,会发送事件并且结束。当收到一个interruped事件,这个新的信号生成器也会中断
scopedExample("`materialize`") {
SignalProducer<Int, NoError>(values: [ 1, 2, 3, 4 ])
.materialize()
.startWithNext { value in
print(value)
}
}
--- `materialize` ---
NEXT 1
NEXT 2
NEXT 3
NEXT 4
COMPLETED
// 注意 value 如果不做materialize就是Int类型
// 现在是Event<Int,NoError>类型,也就是一个monad
sampleOn
当Sampler(被操作的信号生成器)发送任何事件的时候,都转发原来信号生成器的最后一个值
如果当一个sampler (被操作的信号生成器)启动是,当前的值没有被观察者,没有任何事情发生
新产生的信号生成器从源信号生成器哪里发送数据,如果两个信号生成器任何一个complete或者interrupt 新产生的都会中断
/*:
### `sampleOn`
Forwards the latest value from `self` whenever `sampler` sends a Next
event.
If `sampler` fires before a value has been observed on `self`, nothing
happens.
Returns a producer that will send values from `self`, sampled (possibly
multiple times) by `sampler`, then complete once both input producers have
completed, or interrupt if either input producer is interrupted.
*/
scopedExample("`sampleOn`") {
let baseProducer = SignalProducer<Int, NoError>(values: [ 1, 2, 3, 4 ])
let sampledOnProducer = SignalProducer<Int, NoError>(values: [ 1, 2 ])
.map { _ in () }
let newProduce = baseProducer
.sampleOn(sampledOnProducer)
newProduce .startWithNext { value in
print(value)
}
}
--- `sampleOn` ---
4
4
sampler发送的2次值都被变换成baseProduce 的comlete前的最后一个值
combinePrevious
向前合并,每发送一个值就结合历史发送数据的最后一个构造成一个新的元组返回。在第一个发送时由于没有历史数据,所以combinePrevioud传递了一个默认值。当作第一次的合并
scopedExample("`combinePrevious`") {
SignalProducer<Int, NoError>(values: [ 1, 2, 3, 4 ])
.combinePrevious(42)
.startWithNext { value in
print("\(value)")
}
}
--- `combinePrevious` ---
(42, 1) // 第一次没有历史记录默认值是42
(1, 2) // 第二次默认记录是1
(2, 3)
(3, 4)
scan
类似reduce,将值聚合为一个新的值,每次聚合都保留结果作为下次的默认值。首次需给出默认值。
每次聚合都会发送这个值
scopedExample("`scan`") {
SignalProducer<Int, NoError>(values: [ 1, 2, 3, 4 ])
.scan(0, +)
.startWithNext { value in
print(value)
}
}
--- `scan` ---
1
3
6
10
reduce
和scan类似 ,区别为reduce只发送聚合后的值并且立即结束
scopedExample("`reduce`") {
SignalProducer<Int, NoError>(values: [ 1, 2, 3, 4 ])
.reduce(0, +)
.startWithNext { value in
print(value)
}
}
--- `reduce` ---
10
skipRepeats
跳过表达式里返回true的值,第一个值不会被跳过
scopedExample("`skipWhile`") {
SignalProducer<Int, NoError>(values: [ 3, 3, 3, 3, 1, 2, 3, 4 ])
.skipWhile { $0 > 2 }
.startWithNext { value in
print(value)
}
}
--- `skipRepeats` ---
1
2
3
1
2
4
1
// 注意并不是去重,只是两两比较,true的值被忽略
skipWhile
对每个值都去做判断,直到返回faslse,之前的值会被跳过
scopedExample("`skipWhile`") {
SignalProducer<Int, NoError>(values: [ 3, 3, 3, 3, 1, 2, 3, 4 ])
.skipWhile { $0 > 2 }
.startWithNext { value in
print(value)
}
}
--- `skipWhile` ---
1 // 到1 返回false 之前的值被忽略掉
2
3
4
takeUntilReplacement
在被替换的信号发生器发送信号之后,发送被替换的信号。
scopedExample("`takeUntilReplacement`") {
let (replacementSignal, incomingReplacementObserver) = Signal<Int, NoError>.pipe()
let baseProducer = SignalProducer<Int, NoError> { incomingObserver, _ in
incomingObserver.sendNext(1)
incomingObserver.sendNext(2)
incomingObserver.sendNext(3)
// 下面被替换的信号生成器发送了事件,之后就不再发送baseProducer的事件了
// 相当于被替换了
incomingReplacementObserver.sendNext(42)
incomingObserver.sendNext(4)
incomingReplacementObserver.sendNext(42)
}
let producer = baseProducer.takeUntilReplacement(replacementSignal)
producer.startWithNext { value in
print(value)
}
}
--- `takeUntilReplacement` ---
1
2
3
42
42
takeLast
在发送complete事件后支取count此数据
scopedExample("`takeLast`") {
SignalProducer<Int, NoError>(values: [ 1, 2, 3, 4 ])
.takeLast(2)
.startWithNext { value in
print(value)
}
}
只取了2次数据
--- `takeLast` ---
3
4
ignoreNil
如果发送的事件是可选类型,解包这些可选类型,并且丢弃nil值
scopedExample("`ignoreNil`") {
SignalProducer<Int?, NoError>(values: [ nil, 1, 2, nil, 3, 4, nil ])
.ignoreNil()
.startWithNext { value in
print(value)
}
}
--- `ignoreNil` ---
1
2
3
4
zipWith
压缩信号生成器,只有在两个信号都有数据发送之后,新的信号生成器才会发送数据。
新的数据被组合为元组。
scopedExample("`zipWith`") {
let baseProducer = SignalProducer<Int, NoError>(values: [ 1, 2, 3, 4 ])
let zippedProducer = SignalProducer<Int, NoError>(values: [ 42, 43 ])
baseProducer
.zipWith(zippedProducer)
.startWithNext { value in
print("\(value)")
}
}
--- `zipWith` ---
(1, 42)
(2, 43)
后面因为第二个没有数据了所以不会在聚合了
times
time(count) 重复发送count数据,每次重复必须上次发送完成事件
scopedExample("`times`") {
var counter = 0
SignalProducer<(), NoError> { observer, disposable in
counter += 1
observer.sendCompleted()
}
.times(42)
.start()
print(counter)
}
--- `times` ---
42
retry
如果收到失败事件重试retry(count)次
scopedExample("`retry`") {
var tries = 0
SignalProducer<Int, NSError> { observer, disposable in
if tries == 0 {
tries += 1
observer.sendFailed(NSError(domain: "retry", code: 0, userInfo: nil))
} else {
observer.sendNext(42)
observer.sendCompleted()
}
}
.retry(1)
.startWithResult { result in
print(result)
}
}
--- `retry` ---
.Success(42)
then
当第一个信号发送complete时,第二个信号被替换到信号发送线路上,如果有任何失败事件,后面的就替换失败。
第一个信号发送的所有事件都会被忽略
这个没有使用场景比较难理解。看图
scopedExample("`then`") {
let baseProducer = SignalProducer<Int, NoError>(values: [ 1, 2, 3, 4 ])
let thenProducer = SignalProducer<Int, NoError>(value: 42)
baseProducer
.then(thenProducer)
.startWithNext { value in
print(value)
}
}
--- `then` ---
42
replayLazily
创建一个新的SignaProduce,内部包含了一个生产者去组播的发送事件。直到capacity(可以保留的数量)。这意味者所有的观者者看到是相同版本的值或者错误。
类似RAC-OC部分中的replayLazily ,底层生产者是懒加载的。第一次被观察的时候才会启动。在启动时所有的被缓存的值将会被直接发送。
如果你发现你需要实时的值,建议你使用Signal(热信号)代替,这个信号默认会缓存一些值,在某些时候这些缓存值无用。
使用方法类似SignalProducer.buffer(count)
这时候冷信号变成了热信号
补充: OC部分中的replay是直接启动的,replayLazily是懒加载的,Swift部分没有直接启动的
scopedExample("`replayLazily`") {
let baseProducer = SignalProducer<Int, NoError>(values: [ 1, 2, 3, 4, 42 ])
.replayLazily(2)
baseProducer.startWithNext { value in
print(value)
}
baseProducer.startWithNext { value in
print(value)
}
baseProducer.startWithNext { value in
print(value)
}
}
--- `replayLazily` ---
1
2
3
4
42
4
42
4
42
flatMap(.Latest)
将收到的每个事件 都映射为新的Producer,然后摊平
,如果原来的producer发送失败,新产生也的立即失败。
scopedExample("`flatMap(.Latest)`") {
SignalProducer<Int, NoError>(values: [ 1, 2, 3, 4 ])
.flatMap(.Latest) { SignalProducer(value: $0 + 3) }
.startWithNext { value in
print(value)
}
}
--- `flatMap(.Latest)` ---
4
5
6
7
flatMapError
把收到的failure事件映射为新的Producer,并且摊平它
scopedExample("`flatMapError`") {
SignalProducer<Int, NSError>(error: NSError(domain: "flatMapError", code: 42, userInfo: nil))
.flatMapError { SignalProducer<Int, NoError>(value: $0.code) }
.startWithNext { value in
print(value)
}
}
--- `flatMapError` ---
42
sampleWith
在sampler发送nextEvents对源Producer的最后一次值组合形成一个新的元组,如果sample发送的时候,源Producer没有任何事件,则什么都不发生。
一旦源Producer和Sampler都任何一个发送了complete或者interruperd事件,则新产生的Producer理解结束。
/*:
### `sampleWith`
Forwards the latest value from `self` with the value from `sampler` as a tuple,
only when `sampler` sends a Next event.
If `sampler` fires before a value has been observed on `self`, nothing happens.
Returns a producer that will send values from `self` and `sampler`,
sampled (possibly multiple times) by `sampler`, then complete once both
input producers have completed, or interrupt if either input producer is interrupted.
*/
scopedExample("`sampleWith`") {
let producer = SignalProducer<Int, NoError>(values: [ 1, 2, 3, 4 ])
let sampler = SignalProducer<String, NoError>(values: [ "a", "b" ])
let result = producer.sampleWith(sampler)
result.startWithNext { left, right in
print("\(left) \(right)")
}
}
--- `sampleWith` ---
4 a
4 b
logEvents
把所有收到的事件都输出一份日志。
scopedExample("`log events`") {
let baseProducer = SignalProducer<Int, NoError>(values: [ 1, 2, 3, 4, 42 ])
baseProducer
.logEvents(identifier: "Playground is fun!")
.start()
}
[Playground is fun!] Started fileName: /var/folders/d_/9kczd9ld7c3ckq_prb99wnn00000gn/T/lldb/31540/playground103.swift, functionName: __lldb_expr_103, lineNumber: 811
[Playground is fun!] Next 1 fileName: /var/folders/d_/9kczd9ld7c3ckq_prb99wnn00000gn/T/lldb/31540/playground103.swift, functionName: __lldb_expr_103, lineNumber: 811
[Playground is fun!] Next 2 fileName: /var/folders/d_/9kczd9ld7c3ckq_prb99wnn00000gn/T/lldb/31540/playground103.swift, functionName: __lldb_expr_103, lineNumber: 811
[Playground is fun!] Next 3 fileName: /var/folders/d_/9kczd9ld7c3ckq_prb99wnn00000gn/T/lldb/31540/playground103.swift, functionName: __lldb_expr_103, lineNumber: 811
[Playground is fun!] Next 4 fileName: /var/folders/d_/9kczd9ld7c3ckq_prb99wnn00000gn/T/lldb/31540/playground103.swift, functionName: __lldb_expr_103, lineNumber: 811
[Playground is fun!] Next 42 fileName: /var/folders/d_/9kczd9ld7c3ckq_prb99wnn00000gn/T/lldb/31540/playground103.swift, functionName: __lldb_expr_103, lineNumber: 811
[Playground is fun!] Completed fileName: /var/folders/d_/9kczd9ld7c3ckq_prb99wnn00000gn/T/lldb/31540/playground103.swift, functionName: __lldb_expr_103, lineNumber: 811
[Playground is fun!] Terminated fileName: /var/folders/d_/9kczd9ld7c3ckq_prb99wnn00000gn/T/lldb/31540/playground103.swift, functionName: __lldb_expr_103, lineNumber: 811
[Playground is fun!] Disposed fileName: /var/folders/d_/9kczd9ld7c3ckq_prb99wnn00000gn/T/lldb/31540/playground103.swift, functionName: __lldb_expr_103, lineNumber: 811