如何在Swift中将组件与通道或等效的消息总线实现分离?
作为来自Clojure的Swift初学者,我习惯于在启动组件时返回core.async通道,然后将其连接到调用方以进行控制流。
我看到Swift中有一个叫做DispatchQueue
的东西,但这看起来不像消息总线,而且似乎没有缓冲。
具体来说,我正在控制iOS上的音频子系统,我需要通过可插拔架构发送惰性信号。
答案 0 :(得分:0)
我发现DispatchQueue
中的a simple event bus implementation封装了notify
和async
方法,并具有一定的锁定功能,以防止多个订阅者在forEach期间发生突变(请参阅Protected.swift ),但我怀疑无需另外锁定即可安全地完成此操作:
//
// Channel.swift
// Lightning
//
// Created by Göksel Köksal on 5.03.2018.
// Copyright © 2018 GK. All rights reserved.
//
import Foundation
/// An event bus object which provides an API to broadcast messages to its subscribers.
public class Channel<Value> {
internal class Subscription {
weak var object: AnyObject?
private let queue: DispatchQueue?
private let block: (Value) -> Void
var isValid: Bool {
return object != nil
}
init(object: AnyObject?, queue: DispatchQueue?, block: @escaping (Value) -> Void) {
self.object = object
self.queue = queue
self.block = block
}
func notify(_ value: Value) {
if let queue = queue {
queue.async { [weak self] in
guard let strongSelf = self else { return }
if strongSelf.isValid {
strongSelf.block(value)
}
}
} else {
if isValid {
block(value)
}
}
}
}
internal var subscriptions: Protected<[Subscription]> = Protected([])
/// Creates a channel instance.
public init() { }
/// Subscribes given object to channel.
///
/// - Parameters:
/// - object: Object to subscribe.
/// - queue: Queue for given block to be called in. If you pass nil, the block is run synchronously on the posting thread.
/// - block: Block to call upon broadcast.
public func subscribe(_ object: AnyObject?, queue: DispatchQueue? = nil, block: @escaping (Value) -> Void) {
let subscription = Subscription(object: object, queue: queue, block: block)
subscriptions.write { list in
list.append(subscription)
}
}
/// Unsubscribes given object from channel.
///
/// - Parameter object: Object to remove.
public func unsubscribe(_ object: AnyObject?) {
subscriptions.write { list in
if let foundIndex = list.index(where: { $0.object === object }) {
list.remove(at: foundIndex)
}
}
}
/// Broadcasts given value to subscribers.
///
/// - Parameters:
/// - value: Value to broadcast.
/// - completion: Completion handler called after notifing all subscribers.
public func broadcast(_ value: Value) {
subscriptions.write(mode: .sync) { list in
list = list.filter({ $0.isValid })
list.forEach({ $0.notify(value) })
}
}
}
enum Message {
case didUpdateTheme(Theme)
}
let settingsChannel = Channel<Message>()
class SomeView {
func load() {
settingsChannel.subscribe(self) { message in
// React to the message here.
}
}
}
let view = SomeView()
view.load()
settingsChannel.broadcast(.didUpdateTheme(.light))