Event
基本构成
event: 事件流的基本传递单元
事件流的构成:任意个value event
,以及一个可选的终止事件,包括completed
、failed
、interrupted
。终止以后,就不会在接收。
signal
和signalProducer
都send event
事件形式
-
value event
可以携带多个值,这些值必须同一类型,其他没什么限制。例如value
可以表示集合,进度等,甚至可以使用( )
表示不关心的内容。 -
failure event
表示失败,内含失败信息,应尽快传递出来以做处理。区别于value event
的完成事件,failure event
用来表示那些非正常情况下的终止。如果一个事件流不会永远不会失败,可以使用NoError
类型,来保证不会被放在stream
上 -
completed
表示成功或者事件正常结束。可以使用completed
来缩短和延长一个事件流的lifetime
。例如take
可以在接收几个值后过早结束,而其他方法大多是在接收completed
时结束。 -
interrupted
表示事件中断。大多方法会立即传递处理,但是也有flattening
会忽略interrupted
。系统会自动向disposal
发送interrupted
,当然也可以手动发送,要确保interrupted
会被监测并进行处理。`
连续
event
是连续的,即使多个线程中同时发生,ReactiveSwift
也会保证event
按照顺序接收。
递归
就像保证event
不会同时传递,也保证value event
不会被递归传递。所以operators
和observers
都不可重入(reentrant
)。
这是与rac oc版最大的区别
如果一个线程正在处理一个信号,而此时将一个value event
传入这个信号,会造成死锁。
注意terminal event
是允许被递归发送的,但要配合delay
使用,以保证不是一个正在处理的事件发送出来。
同步
信号处理为同步,在结束处理后才继续执行其他。类似于NSNotificationCenter
和UIControl
Signal
概念
signal:单向的事件流。
原文:A signal is a stream of values that obeys the Event contract.
信号是引用类型,不同的信号有不同的lifetime
,可以被终止,一旦终止无法重启。
初始化
信号在初始化完成后开始工作,如果在信号初始化完成前,一样可以发送event
,但是无法被接收到。
Observer
Observers:在任何时间地点注册订阅他们所感兴趣的内容,但无法影响内容本身
就像电视信号,你可以收看,但是无法改变其中内容,也无法影响电视信号自身的开始或结束。
let channel: Signal<Program, NoError> = tvStation.channelOne
channel.observeValues { program in ... }
也就是说,observer
的添加和移除并不会影响signal
的启动或者停止。只有terminating event
和disposal
才会停止signal
。
多个Observer
类似于电视信号,当多个observer
观察同一signal
时,这个信号并不会针对某个observer
做特殊处理,所有observer
都是统一按照事件流顺序进行接收。
只有一个特例,是在信号终止后添加observer
会收到interrupted event
。
生命周期
signal
需要一个observer
来公开持有,不需要事件流中的event
,所有只要有一个active observer
,signal
就会一直retain
不会释放。
换句话说,当信号没有被持有和观察,就会被释放掉。
释放
当终止事件信号传入时,所有的observer
将会被释放,并且产生的事件也会被处理掉。
最简单的方式是使用disposable
SignalProducer
概念
信号产生器,值类型,像“食谱”一样,只是描述信号如何产生,并只在信号产生后才开始工作。
流程
SignalProducer
在start
和startWithSignal
方法执行时开始产生信号。产生信号后,开始执行SignalProducer
初始化时的闭包内容,发送相应信号内容,最后在observer
接收到相应内容,并对事件流的内容做相关处理。
虽然SignalProducer
并不是真正的执行某项工作,而是被描述为开始或者停止信号产生器,但是这种说法表示将会产生一个信号并开始工作或者停止。
多个observer
信号产生器可以开始多次,并且相应的闭包内容也可以被执行多次。
当两次start
绑定两个observer
时,相应闭包内容会执行两次,但是每个observer相应监测方法只会被执行一次。
也就是说虽然多个observer
绑定在同一个producer
以及同一个闭包方法,但实际上由于每次start
产生新的signal
,所以绑定在不同的signal
上,在不同的时间线上分别执行闭包内容。
Lift
使用lift
用signal
产生signal procueder
Disposable
使用start
和startWithSignal
来创建signal
的同时,会自动产生disposable
,通过发送interrupted
来中断observer
并执行disposable
。同时绑定这个新号的其他内容也会被释放。
注意只会影响这个信号本事相应的内容,并不会影响同一个产生器产生的其他信号。
实例
signalProducer: 创建一个带有值的信号流来延迟工作,直到执行start
方法。
任意的start
方法都会唤醒signalProducer
来产生signal
,随后执行对应的工作。
这就像一段可选的视频流,你可以决定看什么,什么时间开始看以及什么时间终止。
//定义信号产生器
let frames: SignalProducer<VideoFrame, ConnectionError> = vidStreamer.streamAsset(id: tvShowId)
//信号产生器开始执行产生信号并返回一个中断者(用来随时中断)
let interrupter = frames.start { frame in ... }
//停止
interrupter.dispose()
Property
概念
property
是含有初始值的一种信号,与所绑定值的关系比signal
更加牢固,总可以监测并获取到值的最新状态,并且不会失败。始终观察某值变化,类似于KVO
类似于视频播放时候随时更新的视频时间进度条。
实例
let currentTime: Property<TimeInterval> = video.currentTime
print("Current time offset: \(currentTime.value)")
currentTime.signal.observeValues { timeBar.timeLabel.text = "\($0)" }
observer
property
必须能够获取或者存储属性的最新值,并且通过propertyProtocol.value
访问。pbserver
相当于didset observer
。
composed property
注意property
绑定属性不会影响到源属性,这点类似于绑定观察信号不会影响源信号一样。
composed property
没有自己的lifetime
,并且他的释放不会影响到相应的signal
或者producer
。
Action
action: 预设行动完成连串工作。
当被一个输入唤醒时,传入这个输入的最新状态,并通过一连串的处理返回想要的结果。
这像一个自动贩卖机,投入硬币,选择想要的东西,需要注意的是一台贩卖机不能同时服务两位客人。
// Purchase from the vending machine with a specific option.
//VC中调用,传参,UI处理返回结果。
vendingMachine.purchase
.apply(snackId)
.startWithResult { result
switch result {
case let .success(snack):
print("Snack: \(snack)")
case let .failure(error):
// Out of stock? Insufficient fund?
print("Transaction aborted: \(error)")
}
}
// The vending machine.
class VendingMachine {
//定义viewmodel中提供购买请求接口
let purchase: Action<Int, Snack, VendingMachineError>
let coins: MutableProperty<Int>
// The vending machine is connected with a sales recorder.
init(_ salesRecorder: SalesRecorder) {
coins = MutableProperty(0)
//实现购买接口
purchase = Action(state: coins, enabledIf: { $0 > 0 }) { coins, snackId in
return SignalProducer { observer, _ in
//内部处理逻辑
// The sales magic happens here.
// Fetch a snack based on its id
}
}
// The sales recorders are notified for any successful sales.
purchase.values.observeValues(salesRecorder.record)
}
}
LifeTime
lifeTime: 订阅者生命周期。
订阅signal
和signalProducer
时,如果订阅停止,将不再发送信号。
好比停止看视频时,会通过lifeTime
自动关闭这个视频流。
class VideoPlayer {
private let (lifetime, token) = Lifetime.make()
func play() {
let frames: SignalProducer<VideoFrame, ConnectionError> = ...
frames.take(during: lifetime).start { frame in ... }
}
}
官方例子
//定义string类型信号监测textfield中变化值,实际为Signal<String?, Result.NoError>类型
let searchStrings = textField.reactive.continuousTextValues
//搜索结果 数组
let searchResults = searchStrings
.flatMap(.latest) { (query: String?) -> SignalProducer<(Data, URLResponse), AnyError> in
//第一步筛选返回signalProducer
let request = self.makeSearchRequest(escapedQuery: query)
return URLSession.shared.reactive.data(with: request)
}
.map { (data, response) -> [SearchResult] in
//第二步筛选返回结果数组
let string = String(data: data, encoding: .utf8)!
return self.searchResults(fromJSONString: string)
}
.observe(on: UIScheduler())//回到主线程