这篇文章讲解在ReactiveCocoa中关于Signal的一些常用的操作,涉及到信号的转换操作等。
一.Signal可以通过oberver函数来监听数据流中值的变化
let (signal, observer) = Signal<String, NoError>.pipe()
signal.observe { event in
switch event {
case let .value(value): //当Observer 发送一个value事件的时候 执行
print("Value: \(value)")
case let .failed(error): //当Observer 发送一个Error事件的时候 执行
print("Failed: \(error)")
case .completed: // 当Observer 发送Completed事件的时候 执行
print("Completed")
case .interrupted: // 当Observer 发送Interrupted事件的时候 执行
print("Interrupted")
}
}
observer.send(value: "1") //print Value: 1
observer.sendCompleted() //print Completed
observer.send(value: "2") // no print
这里我们需要注意的是 Completed 和 Interrupted 代表当前事件流的结束。
二.事件流的转换操作,这些操作会将当前事件流转换为一个新的事件流。
2.1 signal.map操作被用于将一个具有values的事件流 转换为一个新的具有results的事件流。
let (signal, observer) = Signal<String, NoError>.pipe()
signal
.map { string in
return string.uppercased() //在这里一定要return 一个value
}
.observeValues { value in print(value) }
observer.send(value: "a") // Prints A
observer.send(value: "b") // Prints B
observer.send(value: "c") // Prints C
//需要注意的是 在map函数中,一定要显示的return一个value,否则signal将无法继续传递,
//因为它不知道当前value经过map操作转换成了什么。
2.2 signal. filter操作用于过滤某些signal。在某些场景中,可能会产生一些我们不想处理的signal,这时我们可以用filter来过滤掉我们不想处理的signal。就好比过滤掉数组中某些我们不想要的数据。接下来,我们举个例子,用来过滤掉value为奇数的signal的例子
let (signal, observer) = Signal<Int, NoError>.pipe()
signal
.filter { number in number % 2 == 0 }
.observeValues { value in print(value) }
observer.send(value: 1) // Not printed
observer.send(value: 2) // Prints 2
observer.send(value: 3) // Not printed
observer.send(value: 4) // prints 4
需要注意的是 filter函数必须return一个bool类型的数据,当值为true的时候, signal将会被保留,
值为false的时候,signal将会被过滤掉。
2.3 signal.reduce函数,用来将事件流中所有的数据结合为一个新的value.注意的是这个最终的value仅仅会在输入流complete的时候被发送, 当然如果事件流interrupted,我们同样不会得到任何结果。例如: 我们对一个int数据流中的所有value进行求和,获得最终的结果
let (signal, observer) = Signal<Int, NoError>.pipe()
//reduce需要传递一个初始的结果,和javascript中数组的reduce函数相似
//例如求和可以给初始值为0, 求积可以初始值为1
signal
.reduce(0) { $0 + $1 }
.observeValues { value in print(value) }
observer.send(value: 1) // nothing printed
observer.send(value: 2) // nothing printed
observer.send(value: 3) // nothing printed
observer.sendCompleted() // prints 6
2.4 signal.collect() 操作用于聚合事件流中values,转换成为一个array value.同样这个最终的 array value仅仅会在输入流complete的时候被发送。
let (signal, observer) = Signal<Int, NoError>.pipe()
signal
.collect()
.observeValues { value in print(value) }
observer.send(value: 1) // nothing printed
observer.send(value: 2) // nothing printed
observer.send(value: 3) // nothing printed
observer.sendCompleted() // prints [1, 2, 3]
三:事件流的组合方式(combineLatest, zip, merge, concat, latest)
3.1 Signal.combineLatest函数,可以用来结合两个或多个事件流的最新值。结合的事件流中,必须每个事件流都发送了一个值,这时Signal.combineLatest函数产生的新的事件流才会有结果输出。在此之后,任何一个结合的事件流有新值发送时,都会导致结合后的事件流有新值输出。当然,在这过程中 如果有siganl被Interrupted,那么结合后的事件流将会立即产生Interrupted响应。
let (numbersSignal, numbersObserver) = Signal<Int, NoError>.pipe()
let (lettersSignal, lettersObserver) = Signal<String, NoError>.pipe()
let signal = Signal.combineLatest(numbersSignal, lettersSignal)
signal.observeValues { next in print("Next: \(next)") }
signal.observeCompleted { print("Completed") }
numbersObserver.send(value: 0) // nothing printed
numbersObserver.send(value: 1) // nothing printed
lettersObserver.send(value: "A") // prints (1, A)
numbersObserver.send(value: 2) // prints (2, A)
numbersObserver.sendCompleted() // nothing printed
lettersObserver.send(value: "B") // prints (2, B)
lettersObserver.send(value: "C") // prints (2, C)
lettersObserver.sendCompleted() // prints "Completed"
let (numbersSignal, numbersObserver) = Signal<Int, NoError>.pipe()
let (lettersSignal, lettersObserver) = Signal<String, NoError>.pipe()
let signal = Signal.combineLatest(numbersSignal, lettersSignal)
signal.observeValues { next in print("Next: \(next)") }
signal.observeCompleted { print("Completed") }
signal.observeInterrupted {
print("Interrupted")
}
numbersObserver.send(value: 0) // nothing printed
numbersObserver.send(value: 1) // nothing printed
lettersObserver.send(value: "A") // prints (1, A)
numbersObserver.send(value: 2) // prints (2, A)
numbersObserver.sendInterrupted() // prints Interrupted
lettersObserver.send(value: "B") // nothing printed
lettersObserver.send(value: "C") // nothing printed
3.2 Signal.zip函数将两个或多个事件流的值成对的组合起来,产生一个数据类型为tuple的输出流.那么这个成对我们怎么理解呢?也就是说任何第n个元祖的元素对应的是输入流的第n个元素,这意味着直到每个输入流都发送了第n个值,输出流才会发送第n个值。
zip函数在实际开发中应用比较多,比如某个页面加载完毕以后,需要同时请求两个数据接口,只有当两个数据接口均响应回数据以后,再去刷新页面。这个时候 我们就可以把每个请求数据接口当做一个输入流事件,然后对这两个输入流事件进行zip,当数据响应回来以后发送value事件,这时通过zip后的事件的observer函数 进行监听,刷新页面。
let (numbersSignal, numbersObserver) = Signal<Int, NoError>.pipe()
let (lettersSignal, lettersObserver) = Signal<String, NoError>.pipe()
let signal = Signal.zip(numbersSignal, lettersSignal)
signal.observeValues { next in print("Next: \(next)") }
signal.observeCompleted { print("Completed") }
numbersObserver.send(value: 0) // nothing printed
numbersObserver.send(value: 1) // nothing printed
lettersObserver.send(value: "A") // prints (0, A)
numbersObserver.send(value: 2) // nothing printed
numbersObserver.sendCompleted() // nothing printed
lettersObserver.send(value: "B") // prints (1, B)
lettersObserver.send(value: "C") // prints (2, C) & "Completed"
// zip后的输出流的Completed事件什么时候执行呢?在我看来,
//当某个输入流所输入的值都已经被zip后的输出流所发送,
//然后该输入流发送Completed事件,那么输出流就会触发Completed事件。
//如果该输入流还有value未被输出流所发送,
//那么即使输入流发送Completed事件,输出流也无法触发Completed事件。
//如果大家有不同意见,欢迎评论区提出你的论证。
3.3 signal.flatten操作 用于将多个流碾压为一个单一的流。最终单一的流变成外部流的结果。
let values = [
[ 1, 2, 3 ], // siganl 1
[ 4, 5, 6 ], // siganl 2
[ 7, 8 ], // // siganl 3
]
//以下为3种不同的碾压策略对应的结果
let merge =
[ 1, 4, 2, 7,5, 3,8,6 ]
let concat =
[ 1, 2, 3,4, 5, 6,7, 8]
let latest =
[ 1, 4, 7, 8 ]
3.3.1 signal.flatten(.merge),多个事件流的merge操作会将内部事件流的每个值转发给外部事件流。
let (lettersSignal, lettersObserver) = Signal<String, NoError>.pipe()
let (numbersSignal, numbersObserver) = Signal<String, NoError>.pipe()
let (signal, observer) = Signal<Signal<String, NoError>, NoError>.pipe()
//将signal的内部事件流进行合并,
signal.flatten(.merge).observeValues { print($0) }
observer.send(value: lettersSignal)
observer.send(value: numbersSignal) //将lettersSignal,numbersSignal 添加到signal的内部事件流
//signal则为外部事件流
observer.sendCompleted()
lettersObserver.send(value: "a") // prints "a"
numbersObserver.send(value: "1") // prints "1"
lettersObserver.send(value: "b") // prints "b"
numbersObserver.send(value: "2") // prints "2"
lettersObserver.send(value: "c") // prints "c"
numbersObserver.send(value: "3") // prints "3"
3.3.2signal.flatten(.concat) 操作,用来序列化内部事件流的事件。怎么理解这个策略呢,其实你可以考虑一下javascript中数组的concat操作,它是将两个数组中的数据拼接成一个数组,例如: [1, 2, 3].concat([4,5,6]) = [1,2,3,4,5,6]。所以,signal中的concat同样的道理,将多个内部信号的值拼接成一个单一的数据流,它的拼接策略是当第一个内部信号发送complete以后,外部信号才会接受第二个内部信号发送的值,所以 内部信号的顺序很重要。
let (lettersSignal, lettersObserver) = Signal<String, NoError>.pipe()
let (numbersSignal, numbersObserver) = Signal<String, NoError>.pipe()
let (signal, observer) = Signal<Signal<String, NoError>, NoError>.pipe()
signal.flatten(.concat).observeValues { print($0) }
observer.send(value: lettersSignal)
observer.send(value: numbersSignal)
observer.sendCompleted()
//因为内部信号的顺序为lettersSignal,numbersSignal 所以外部信号先接收lettersSignal发送的值,
//当lettersSignal发送Completed事件以后,外部信号会依次接收numbersSignal发送的数据
//所以它和数组的concat函数是不是很相似呢?如果大家有些许怀疑,
//可以颠倒一下内部信号的顺序看一下打印事件
numbersObserver.send(value: "1") // nothing printed
lettersObserver.send(value: "a") // prints "a"
lettersObserver.send(value: "b") // prints "b"
numbersObserver.send(value: "2") // nothing printed
lettersObserver.send(value: "c") // prints "c"
lettersObserver.sendCompleted() // nothing printed
numbersObserver.send(value: "3") // prints "3"
numbersObserver.sendCompleted() // nothing printed
3.3.3signal.flatten(. latest) 操作,从最新的事件流转发值。那么怎么理解这个策略呢,当我们向外部事件流中添加多个内部事件流时,多个内部事件流只有最后一个事件流转发值才会被外部事件流接收。
let (lettersSignal, lettersObserver) = Signal<String, NoError>.pipe()
let (numbersSignal, numbersObserver) = Signal<String, NoError>.pipe()
let (signal, observer) = Signal<Signal<String, NoError>, NoError>.pipe()
signal.flatten(.latest).observeValues { print($0) }
observer.send(value: lettersSignal) // nothing printed
//此时signal中只有一个lettersSignal内部事件流,
//因为numbersSignal此时还不是signal的内部事件流,所以signal不会收到numbersObserver发送的数据
numbersObserver.send(value: "1") // nothing printed
//因为lettersSignal此时为signal中唯一的内部事件流,所以signal会接收到lettersObserver发送的数据
lettersObserver.send(value: "a") // prints "a"
lettersObserver.send(value: "b") // prints "b"
numbersObserver.send(value: "2") // nothing printed
//此时numbersSignal会将signal中lettersSignal内部事件流给替换掉,
//signal只会接收numbersObserver发送的数据,
//而不会在接收lettersObserver发送的数据. 这就是最新(latest)策略
observer.send(value: numbersSignal) // nothing printed
lettersObserver.send(value: "c") // nothing printed
numbersObserver.send(value: "3") // prints "3"
文章内容参考链接: https://github.com/ReactiveCocoa/ReactiveSwift/blob/master/Documentation/BasicOperators.md。以上内容如有错误或不严谨的地方,欢迎大家在评论区指出问题所在,谢谢!