你應(yīng)該知道的5個(gè)Swift組合變換操作符
本文轉(zhuǎn)載自公眾號(hào)“讀芯術(shù)”(ID:AI_Discovery)。
想隨時(shí)隨地輕松變更數(shù)據(jù)格式?本文將教你5種解法!我將在Xcode Playground中創(chuàng)建示例函數(shù),運(yùn)行它們并觀察結(jié)果。
1. map
.map 操作符允許我們轉(zhuǎn)換閉包中來(lái)自發(fā)布者的所有元素。
- var subscriptions =Set<AnyCancellable>()
- funcmapExample() {
- let subject =PassthroughSubject<Int, Never>()
- subject
- .map { (integer) in
- returnString(integer)
- }
- .sink(receiveValue: {
- print("Value: \($0), Type: \(type(of: $0))")
- })
- .store(in: &subscriptions)
- subject.send(12)
- subject.send(31)
- subject.send(55)
- subject.send(4)
- subject.send(18)
- }
下面是這段代碼的作用:
- 創(chuàng)建一個(gè)接受Int 值的PassthroughSubject。
- 使用.map 操作符將每個(gè)接收到的Int 值轉(zhuǎn)換為String。
- 然后,訂閱發(fā)布者并打印轉(zhuǎn)換后的元素的值和類型。
向受試者發(fā)送隨機(jī)數(shù)以觀察以下結(jié)果:

還有一種巧妙的方法來(lái)使用對(duì)象的鍵路徑獲取對(duì)象的屬性:
- funcmapKeyPathExample() {
- structCarBrand {
- let title:String
- let country:String
- }
- let carBrandsSubject =PassthroughSubject<CarBrand, Never>()
- carBrandsSubject
- .map(\.country)
- .sink(receiveValue: { country in
- print("Country:\(country)")
- })
- .store(in: &subscriptions)
- carBrandsSubject.send(
- CarBrand(title: "MercedesBenz", country: "Germany")
- )
- carBrandsSubject.send(
- CarBrand(title: "Ford", country: "USA")
- )
- carBrandsSubject.send(
- CarBrand(title: "Honda", country: "Japan")
- )
- }
使用.map(\.country),可以訪問(wèn)CarBrand的國(guó)家屬性。然后只需打印每個(gè)國(guó)家:

2. replaceNil
顧名思義,.replaceNil 操作符將每個(gè)接收到的nil元素轉(zhuǎn)換為指定的元素:
- funcreplaceNilExample() {
- let values: [Int?] = [123, nil, nil, 12, 10]
- let valuesvaluesPublisher =values.publisher
- valuesPublisher
- .replaceNil(with: 0)
- .map { $0! }
- .collect()
- .sink(receiveValue: { print($0) })
- .store(in: &subscriptions)
- }
請(qǐng)注意,還可以將多個(gè)操作符組合在一起以達(dá)到必要的結(jié)果。首先將每個(gè)nil 值替換為0,然后強(qiáng)制解開值,最后將所有值收集在一個(gè)數(shù)組中:

需要注意的是在.map 操作符中使用強(qiáng)制展開的方法。如果你不喜歡強(qiáng)行解包該怎么辦?我們還有一個(gè).map協(xié)變量:.compactMap,它能自動(dòng)轉(zhuǎn)發(fā)僅非零的那些元素:
- funcreplaceNilExample() {
- let values: [Int?] = [123, nil, nil, 12, 10]
- let valuesvaluesPublisher = values.publisher
- valuesPublisher
- .replaceNil(with: 0)
- .compactMap { $0 }
- .collect()
- .sink(receiveValue: { print($0) })
- .store(in: &subscriptions)
- }
3. collect
使用.collect操作符可以很容易地收集所有接收到的元素,并發(fā)出一個(gè)包含所有元素的數(shù)組:
- funccollectExample() {
- let integers = [1, 4, 5, 12, 24, 44]
- let integerPublisher =integers.publisher
- integerPublisher
- .collect()
- .sink(receiveValue: { print($0) })
- .store(in: &subscriptions)
- }
于是我們得到了想要的結(jié)果:

注意,發(fā)布者必須發(fā)出.completed事件才能實(shí)現(xiàn)這個(gè)操作,因?yàn)?collect會(huì)一直等待,直到所有元素都發(fā)出并且發(fā)布者完成操作為止。例如,如果使用PassthroughSubject,需要在發(fā)送所有元素后發(fā)送.finished事件:
- funccollectExample() {
- let integerPublisher =PassthroughSubject<Int, Never>()
- integerPublisher
- .collect()
- .sink(receiveValue: { print($0) })
- .store(in: &subscriptions)
- integerPublisher.send(1)
- integerPublisher.send(4)
- integerPublisher.send(5)
- integerPublisher.send(12)
- integerPublisher.send(24)
- integerPublisher.send(44)
- integerPublisher.send(completion: .finished)
- }
4. flatMap
.flatMap操作符允許我們將給定的發(fā)布者轉(zhuǎn)換為另一個(gè)發(fā)布者。來(lái)看看它是如何將觀察結(jié)果從Network更改為isAvailable主題:
- funccollectExample() {
- let integerPublisher =PassthroughSubject<Int, Never>()
- integerPublisher
- .collect()
- .sink(receiveValue: { print($0) })
- .store(in: &subscriptions)
- integerPublisher.send(1)
- integerPublisher.send(4)
- integerPublisher.send(5)
- integerPublisher.send(12)
- integerPublisher.send(24)
- integerPublisher.send(44)
- integerPublisher.send(completion: .finished)
- }
當(dāng)更改它的值時(shí),我們要打印出isAvailable值。首先,它打印初始值(正在使用CurrentValueSubject),一旦為其分配了新值,就會(huì)發(fā)生以下情況:

5. scan
.scan操作符能夠在閉包中公開當(dāng)前發(fā)出的值以及最新的值??梢允褂盟鼇?lái)累積值并打印總結(jié)果:
- funcflatMapExample() {
- structNetwork {
- let title:String
- let isAvailable =CurrentValueSubject<Bool, Never>(false)
- }
- let wifi =Network(title: "Wi-Fi")
- let networkSubject = CurrentValueSubject<Network, Never>(wifi)
- networkSubject
- .flatMap ({
- return$0.isAvailable
- })
- .sink(receiveValue: {
- print("Is networkenabled: \($0)")
- })
- .store(in: &subscriptions)
- wifi.isAvailable.value=true
- wifi.isAvailable.value=false
- }
在這里,執(zhí)行的是以下操作:
- 創(chuàng)建收益數(shù)組(下劃線是將數(shù)字中的千單位分開的好方法)。
- 創(chuàng)建這些收益的發(fā)布者。
- 使用.scan操作符,將當(dāng)前發(fā)出的值($0)添加到從零開始的最新值($1)。
最后,計(jì)算出總收益:
【責(zé)任編輯:趙寧寧 TEL:(010)68476606】