Transform
接触过FP的应该都知道map,RxSwift也提供对应的方法将Element进行变形,主要的方法有buffer
, flatMap
, flatMapFirst
, flatMapLatest
, map
, scan
和window
, 用的比较多的是map
和flatMap
。
map
let disposeBag = DisposeBag()
Observable.of(1, 2, 3)
.map { "\($0)\($0)" }
.subscribeNext { print($0) }
.addDisposableTo(disposeBag)
根据map
的定义:
public func map<R>(selector: E throws -> R) -> Observable<R> {
return self.asObservable().composeMap(selector)
}
selector
这个closure负责将E转换为R,而E这里被定义为Observable.of(1, 2, 3)的Int,每个元素会被转换为String。
flatMap
let disposeBag = DisposeBag()
let sequenceInt = Observable.of(1, 2, 3)
let sequenceString = Observable.of("A", "B", "C", "D")
sequenceInt.flatMap { _ in
return sequenceString
}
.subscribe { print($0) }
.addDisposableTo(disposeBag)
flatMap
的定义:
public func flatMap<O: ObservableConvertibleType>(selector: (E) throws -> O) -> Observable<O.E> {
return FlatMap(source: asObservable(), selector: selector)
}
来看看Rx官方给出的描述:
map | flatMap |
---|---|
Transform the items emitted by an Observable by applying a function to each item | Transform the items emitted by an Observable into Observables, then flatten the emissions from those into a single Observable |
map
和flatMap
的区别在于map
的selector将用于Observable中的每一个Element,操作完成后,Element的数量和源Observable中的是一样的,而flatMap
的selector的调用次数和源Element数量一致,但是由于selector的返回值为一个Observable,所以在操作完成后,得到的Elements为源Element数量*selector返回的Observable的Element数量,然后将所有Element放入一个Observable中,类似于[[1, 2], [2, 3], [3, 4]]
-> [1, 2, 2, 3, 3, 4]
。
flatMapLatest
比起map
和flatMap
,要稍微难理解一点:
let disposeBag = DisposeBag()
[1, 2, 3].toObservable()
.flatMapLatest { value in
return ["\(value)a", "\(value)b"].toObservable()
}
.subscribe { print($0) }
.addDisposableTo(disposeBag)
根据RxJS flatMapLatest这个代码期望的结果是(RxSwift和Rx系的其他语句具有相似接口,其中RxJava对应的方法是switchMap,在RxJS Source Code也能发现其实它和switchMap是一个函数):
Next(1a)
Next(2a)
Next(3a)
Next(3b)
Completed
但是实际上得到的输出结果是:
Next(1a)
Next(1b)
Next(2a)
Next(2b)
Next(3a)
Next(3b)
Completed
出现这种情况的原因在github的Get Start有解释,问题出现在selector中的toObservable
,它的实现使用了Sequence
:
public func toObservable(scheduler: ImmediateSchedulerType? = nil) -> Observable<Generator.Element> {
return Sequence(elements: self, scheduler: scheduler)
}
Sequence
其实是一个同步队列的实现,在subscribe
调用前会生成Next和Completed,因为无论哪种disposable被返回,生成elements的过程都不能被打断。在这种情况下,flatMapLatest
和flatMap
的输出结果一样。
期望的版本:
let disposeBag = DisposeBag()
var charValues: [Variable<Character>] = [Variable("a"), Variable("a"), Variable("a")]
[0, 1, 2].toObservable()
.flatMapLatest { value -> Observable<Character> in
print("Int value: \(value)")
return charValues[value].asObservable()
}
.subscribe { print($0) }
.addDisposableTo(disposeBag)
charValues[2].value = "b"
charValues[0].value = "c" // nothing happen
charValues[1].value = "d" // nothing happen
和flatMap
一样,flatMapLatest
也会新生成一个Observable的队列,但不同的是它不会合并所有的新Observable中的Element,它会switch到最后一个Observable上(switchMap
这个名字感觉更容易让人理解一点),先前建立的Observable将不再被监听,所以代码中charValues只有最后一个Observable还在被subscribe。
scan
let disposeBag = DisposeBag()
Observable.range(start: 1, count: 3)
.scan(0) { $0 + $1 }
.subscribe { print($0) }
.addDisposableTo(disposeBag)
scan
和swift的原生方法reduce很类似(区别在于scan后的Element数量和源Element数量一致,但是reduce只会返回一个Element),在Observable+Single中我们能够找到它的定义:
public func scan<A>(seed: A, accumulator: (A, E) throws -> A) -> Observable<A> {
return Scan(source: self.asObservable(), seed: seed, accumulator: accumulator)
}
seed
的scan开始的初始值,accumulator
的第一个参数为上一次操作的返回值,第二个参数为Observable队列中的Element,然后返回处理结果。