Combine(二) Operators

Combine有很多方便的方法: Operators。这些operators可以把从publisher传出来的values一层一层的过滤/筛选/变型,subscriber就可以拿到自己真正需要的values了。

1、collect()

collect就是把一个一个输出的value全都放在array里,然后等publisher结束后,输出这个array:

["A", "B", "C", "D", "E"].publisher
  .collect()
  .sink(receiveCompletion: { print($0) },
        receiveValue: { print($0) })
    .store(in: &subscriptions)

// ["A", "B", "C", "D", "E"]
// finished

通常我们会限制collect需要收集values的个数,不然无限等着可能浪费内存:

.collect(2)

// ["A", "B"]
// ["C", "D"]
// ["E"]
// finished

2、map()

这和swift中的用法一样

["A", "B", "C"].publisher
    .map { $0 + $0 }
    .sink(receiveCompletion: { print($0) },
        receiveValue: { print($0) })
    .store(in: &subscriptions)

AA
BB
CC
finished

3、map key path

如果输出的value有多个属性,那么也可以用map和keypath来拆分:

[(x: 2, y: 3), (x: 1, y: 5), (x: 2, y: 6)].publisher
    .map(\.x, \.y)
    .sink(receiveCompletion: { print($0) },
        receiveValue: { x, y in print(x + y) })
    .store(in: &subscriptions)

5
6
8
finished

4、tryMap

tryMap就是map的closure里有可能throw,throw的话就筛选掉这个value:

Just("/fake")
    .tryMap { try FileManager.default.contentsOfDirectory(atPath: $0) }
    .sink(receiveCompletion: { print($0) },
        receiveValue: { print($0) })
    .store(in: &subscriptions)

No such file or directory

5、flatMap

如果你有一个publisher发送的value的某些属性也是publisher, 那sink只会receive外面这个publisher发出的value,属性publisher发出的value要是也想接收,那你就可以用flatMap

struct Person {
    let name : String
    var age: CurrentValueSubject<Int, Never>
    
    init(name: String, age: Int) {
        self.name = name
        self.age = CurrentValueSubject(age)
    }
}

var me = Person(name: "Joshua", age: 18)
var you = Person(name: "Dog", age: 28)

let people = PassthroughSubject<Person, Never>()
people
    .flatMap{ $0.age }
    .sink(receiveCompletion: { _ in
        print("people completed")
    }, receiveValue: { val in
        print(val)
    })

people.send(me)
people.send(you)
me.age.value = 19
you.age.value = 30

//输出
18
28
19
30

好好看一下这个例子,我觉得大家应该就都理解了吧,这属于比较复杂的一个operator了。flatMap还可以限制订阅publisher的个数:

var me = Person(name: "Joshua", age: 18)
var you = Person(name: "Dog", age: 28)
var she = Person(name: "SHE", age: 16)

let people = PassthroughSubject<Person, Never>()
people
    .flatMap(maxPublishers: .max(2)) { $0.age }
    .sink(receiveCompletion: { _ in
        print("people completed")
    }, receiveValue: { val in
        print(val)
    })

people.send(me)
people.send(you)
people.send(she)
me.age.value = 19
you.age.value = 30
she.age.value = 17

// 输出和之前那个一样

6、replaceNil(with:)

"人"如其名的这种我就不解释了

["A", nil, "C"].publisher
.replaceNil(with: "null")
.map { $0! }
.sink(receiveCompletion: { print($0) },
    receiveValue: { print($0) })
.store(in: &subscriptions)

A
null
C
finished

7、replaceEmpty(with:)

let empty = Empty<String, Never>()
empty
.replaceEmpty(with: "Empty")
.sink(receiveCompletion: { print($0) },
    receiveValue: { print($0) })
.store(in: &subscriptions)

Empty
finished

这个Empty一般就是用来测试或demo,直接发送.finish

8、reduce(0)

这个和之前文章中介绍的reduce一样。publisher一定要结束才会输出最后值

[1, 2, 3].publisher
    .reduce(0, +)
    .sink(receiveCompletion: { print($0) },
        receiveValue: { print($0) })
    .store(in: &subscriptions)

6
finished

9、scan

scan和reduce差不多,但是scan不是最后才输出值,每个都会输出

[1, 2, 3].publisher
    .scan(0, +)
    .sink(receiveCompletion: { print($0) },
        receiveValue: { print($0) })
    .store(in: &subscriptions)

1
3
6
finished

10、filter

[1, 2, 3].publisher
    .filter { $0 < 2 }
    .sink(receiveCompletion: { print($0) },
        receiveValue: { print($0) })
    .store(in: &subscriptions)

1
finished

11、removeDuplicates()

注意这个并不是所有重复的都去掉,只是publisher当前要发出的value和上个发出的一样的才会去掉。要让系统排重,那value一定是Equatable, 不然你就得自定义相等的条件:

[1, 1, 2].publisher
    .removeDuplicates()
    .sink(receiveCompletion: { print($0) },
        receiveValue: { print($0) })
    .store(in: &subscriptions)

1
2
finished

12、compactMap

跟swift中的一样,会删掉invalid的值

["1", "a", "2"].publisher
    .compactMap { $0 }
    .sink(receiveCompletion: { print($0) },
        receiveValue: { print($0) })
    .store(in: &subscriptions)

1
2
finished

13、ignoreOutput()
只想知道publisher是不是结束了,不care他发出了什么value

14、first() 和 first(where:)

接收到第一个值就取消订阅,可以用where加条件

[1, 2, 3].publisher
    .first { $0 > 1 }
    .sink(receiveCompletion: { print($0) },
        receiveValue: { print($0) })
    .store(in: &subscriptions)

2
finished

15、last()和last(where:)

跟上一个一样,唯一区别是publisher必须结束才会输出,原因不用说了吧

16、dropFirst()

.dropFirst(3)就是不要前3个

17、drop(while:)

直到while的条件符合,都不要!但一旦符合条件了,后面的都要!

[1, 2, 3, 4, 5].publisher
    .drop(while: { $0 < 3 })
    .sink(receiveCompletion: { print($0) },
        receiveValue: { print($0) })
    .store(in: &subscriptions)

3
4
5
finished

18、drop(untilOutputFrom:)

这个其实也很简单理解,就是你有两个publisher,用这个operator的publisher必须要等另一个publisher输出才会输出,之前的都不要了

19、prefix()、prefix(while:)、prefix(untilOutputFrom:)

跟drop的三个正好相反,保留前几个values直到....唯一要注意的是,跟first一样,符合条件之后就cancel

20、prepend()

在publisher输出之前先输出prepend里面的同类型values

[1, 2, 3].publisher
    .prepend(9, 8) // 也可以prepend([9, 8]),结果一样
    .sink(receiveCompletion: { print($0) },
        receiveValue: { print($0) })
    .store(in: &subscriptions)

9
8
1
2
3
finished

还可以prepend(另一个publisher),这样只有prepend的publisher完成输出,本来的publisher才开始输出

21、append()

嗯没错,跟之前相反,但你要注意,只有publisher结束了,才会输出append的values

现写到这儿...还有一半...shit...

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,904评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,581评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,527评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,463评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,546评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,572评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,582评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,330评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,776评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,087评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,257评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,923评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,571评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,192评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,436评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,145评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容