测试已测试 | ✓ |
语言语言 | SwiftSwift |
许可 | MIT |
发布最后发布 | 2017年3月 |
SwiftSwift 版本 | 3.0 |
SPM支持 SPM | ✓ |
由 Patrick Sturm 维护。
Patrick Sturm, [email protected]
SwiftySignals 可在 MIT 许可证下使用。更多信息请参阅 LICENSE 文件。
SwiftySignals 最初是一个包含几个类的简单框架。在 SwiftySignals 2 中,并发完全被忽略,留由用户处理。从 SwiftySignals 3 开始,API 变得更加灵活和线程安全。并发得到了广泛支持。SwiftySignals 3 现在实现了观察者模式,并基于 Apple 的调度框架。
SwiftySignals 的基本概念是可观察对象和观察者。可观察对象能够发送消息,而观察者能够接收并处理消息。一般来说,您无需自己定义可观察对象或观察者。您只需处理辅助类。SwiftySignals 中主要有两种辅助类:
消息源包含一个或多个可观察对象。修改器既是观察者又是可观察对象。可以连接修改器到一个可观察对象。修改器能够过滤、映射、丢弃和生成消息。由于修改器本身就是可观察对象,因此可以将修改器连接到修改器链。消息从消息源开始,沿着修改器链发送。
类 Signal<T>
是消息源的最简单实现。信号可以用于发送类型为 T
的消息。要这样做,可以使用函数 Signal<T>.fire(with: T)
。如果引发一个消息,则将其发送到连接到 Signal<T>.fired
的所有修改器,这被称为终点。
连接修改器到信号相当简单。假设,我们想在消息引发时打印消息。为此,我们可以使用修改器 then
连接到信号的终点 fired
let signal = Signal<Int>(value: 15)
let observables = ObservableCollection()
signal
.fired
.then { print($0) }
.append(to: observables)
signal.fire(with: 20)
需要注意的是,必须将修改器链存储在某个位置。如果不这样做,则由于自动引用计数,修改器链将立即被删除。然而,可以附录到可观察集合中的修改器以使它们保持活动状态。在这种情况下,修改器链将与可观察集合一起被销毁。
在SwiftySignals中定义了一些消息来源。
信号属于Signal<T>
类。信号有一个函数fire(with message: T)
,它将消息发送到所有连接到信号端点fired
的可观察对象。
属性属于Property<T>
类。属性有一个类型为T
的属性value
。每当值被修改时,新值都会被发送到属性的端点didSet
。属性是线程安全的。这意味着可以从所有线程中读取和写入属性的值。但请注意,读取是一个阻塞命令,可能会在某些情况下造成问题。
let property = Property<Int>(value: 0)
let observables = ObservableCollection()
property
.didSet
.then { print("Value changed to \($0)") }
.append(to: observables)
for i in 0..<10 {
property.value += 1
}
计时器信号属于TimerSignal
类。这样的计时器可以看作是带类型Void
的消息的时间触发信号。计时器通过其init(repeats: Bool)
函数配置,并通过fire
激活。计时器以给定的时间间隔触发消息(如果repeats为true,则持续进行)。
let timer = TimerSignal(repeats: true)
let observables = ObservableCollection()
timer
.fired
.then {.print("Timer has been triggered") }
.append(to: observables)
timer.fire(after: Measurement(value: 10, UnitDuration.seconds)
不同的修饰符可以连接到端点。修饰符将可观察对象转换为不同的可观察对象。由于修饰符也是可观察对象,因此可以将不同的修饰符连接在一起。如果连接了一个修饰符,它将自动接收最后发送的消息。
.then
函数将一个修饰符连接到其中,当发送类型为T
的消息时执行操作。定义此类操作有两种方式:
.then(do: (T)->Void)
的闭包通过实例:.then(call: (Object)->((T)->Void)), on: Object)
消息经过操作处理后,会被发送到所有连接到修饰符的可观察对象。
class ViewController { private let property = Property(value: 56) private let observables = ObservableCollection()
override func viewDidLoad() {
property
.didSet
.then(call: ViewController.update, on: self)
property.value = 67
}
func update(value: Int) {
print("Value = \(value)")
}
}
.filter(predicate: (T)->Bool)
函数连接一个修饰符,检查消息是否满足给定的谓词。如果谓词返回值为true
,则将消息发送到所有连接到修饰符的可观察对象。如果谓词返回值为false
,则丢弃消息。
let property = Property(value: 45)
let observables = Observables()
property
.didSet
.filter { $0 >= 100 }
.then { print("Value \($0) is larger or equal than 100") }
.append(to: observables)
.map(transform: (T)->S)
函数连接一个修饰符,将传入的消息类型从T
转换为S
。转换后的消息被发送到所有连接到修饰符的可观察对象。
let property = Property(value: 100)
property
.didSet
.map { 2 * $0 }
.then { print($0) }
.append(to: observables)
.throttle(pause: Measurement<UnitDuration>)
函数连接一个修饰符,如果消息发送得太快,就会丢弃它们。需要在两个消息之间至少有一个定义的时间暂停,这样就不会丢失任何消息。
.discard(first n: Int)
函数连接一个修饰符,丢弃前n个消息。在那之后的所有消息都会发送到连接到修饰符的可观察对象。
函数.distinct()
连接一个丢弃相等的前一条消息的修饰符。此修饰符仅适用于可比较的消息类型。
函数.debounce(timeout: Measurement
连接一个在给定时间(timeout)后发送消息的修饰符。如果在超时达到之前收到新消息,则重置倒计时。请注意,如果处理的消
每个消息来源和修饰符都使用自己的分发队列进行同步。默认情况下,用户定义的闭包(例如在then
、filter
和map
中使用的闭包)将在主队列上执行。
.dispatch(to queue: DispatchQueue)
.dispatch(qos: DispatchQoS)
第一个版本将所有闭包委托给给定的队列。第二个版本将所有闭包委托给具有指定质量服务类的全局分发队列。
如果闭包应在修饰符的同步队列中执行,则可以使用.noDispatch()
函数。
let property = Property(value: 50)
let observables = Observables()
property
.didSet
.dispatch(qos: DispatchQoS.userInitiated)
.then { print("I am running on a global queue.") }
.dispatch(qos: DispatchQoS.main)
.then { print("I am running on the main queue.") }
.append(to: observables)