這篇文章講解在ReactiveCocoa中關于Signal的一些常用的操作凯亮,涉及到信號的轉換操作等罗捎。
一.Signal可以通過oberver函數來監(jiān)聽數據流中值的變化
let (signal, observer) = Signal<String, NoError>.pipe()
signal.observe { event in
switch event {
case let .value(value): //當Observer 發(fā)送一個value事件的時候 執(zhí)行
print("Value: \(value)")
case let .failed(error): //當Observer 發(fā)送一個Error事件的時候 執(zhí)行
print("Failed: \(error)")
case .completed: // 當Observer 發(fā)送Completed事件的時候 執(zhí)行
print("Completed")
case .interrupted: // 當Observer 發(fā)送Interrupted事件的時候 執(zhí)行
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將無法繼續(xù)傳遞宏赘,
//因為它不知道當前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的時候被發(fā)送, 當然如果事件流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的時候被發(fā)送缰揪。
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函數,可以用來結合兩個或多個事件流的最新值葱淳。結合的事件流中钝腺,必須每個事件流都發(fā)送了一個值,這時Signal.combineLatest函數產生的新的事件流才會有結果輸出赞厕。在此之后拍屑,任何一個結合的事件流有新值發(fā)送時,都會導致結合后的事件流有新值輸出坑傅。當然僵驰,在這過程中 如果有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個元素星爪,這意味著直到每個輸入流都發(fā)送了第n個值,輸出流才會發(fā)送第n個值粉私。
zip函數在實際開發(fā)中應用比較多顽腾,比如某個頁面加載完畢以后,需要同時請求兩個數據接口诺核,只有當兩個數據接口均響應回數據以后抄肖,再去刷新頁面。這個時候 我們就可以把每個請求數據接口當做一個輸入流事件窖杀,然后對這兩個輸入流事件進行zip,當數據響應回來以后發(fā)送value事件漓摩,這時通過zip后的事件的observer函數 進行監(jiān)聽,刷新頁面入客。
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事件什么時候執(zhí)行呢管毙?在我看來,
//當某個輸入流所輸入的值都已經被zip后的輸出流所發(fā)送桌硫,
//然后該輸入流發(fā)送Completed事件夭咬,那么輸出流就會觸發(fā)Completed事件。
//如果該輸入流還有value未被輸出流所發(fā)送铆隘,
//那么即使輸入流發(fā)送Completed事件卓舵,輸出流也無法觸發(fā)Completed事件。
//如果大家有不同意見膀钠,歡迎評論區(qū)提出你的論證边器。
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操作會將內部事件流的每個值轉發(fā)給外部事件流忘巧。
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同樣的道理,將多個內部信號的值拼接成一個單一的數據流搓彻,它的拼接策略是當第一個內部信號發(fā)送complete以后如绸,外部信號才會接受第二個內部信號發(fā)送的值嘱朽,所以 內部信號的順序很重要。
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發(fā)送的值怔接,
//當lettersSignal發(fā)送Completed事件以后搪泳,外部信號會依次接收numbersSignal發(fā)送的數據
//所以它和數組的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) 操作岸军,從最新的事件流轉發(fā)值。那么怎么理解這個策略呢瓦侮,當我們向外部事件流中添加多個內部事件流時艰赞,多個內部事件流只有最后一個事件流轉發(fā)值才會被外部事件流接收。
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發(fā)送的數據
numbersObserver.send(value: "1") // nothing printed
//因為lettersSignal此時為signal中唯一的內部事件流方妖,所以signal會接收到lettersObserver發(fā)送的數據
lettersObserver.send(value: "a") // prints "a"
lettersObserver.send(value: "b") // prints "b"
numbersObserver.send(value: "2") // nothing printed
//此時numbersSignal會將signal中l(wèi)ettersSignal內部事件流給替換掉,
//signal只會接收numbersObserver發(fā)送的數據须喂,
//而不會在接收lettersObserver發(fā)送的數據. 這就是最新(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。以上內容如有錯誤或不嚴謹的地方趁蕊,歡迎大家在評論區(qū)指出問題所在坞生,謝謝!