core.async iOS Swift 通道用于解耦组件

core.async channels in iOS Swift for decoupling components

我应该如何将 Swift 中的组件与通道或等效的消息总线实现分离?

作为来自 Clojure 的 Swift 初学者,我习惯于在启动组件时返回 core.async 通道,然后在调用者处连接它以进行控制流。

我看到 Swift 中有一个叫做 DispatchQueue 的东西,但这看起来不像消息总线,而且似乎没有缓冲。

具体来说,我在 iOS 上控制音频子系统,我需要通过可插拔架构发送惰性信号。

我找到了 a simple event bus implementation of DispatchQueue that wraps the notify and async methods with some locking to protect against mutations during forEach on multiple subscribers (see 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: { [=10=].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({ [=10=].isValid })
            list.forEach({ [=10=].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))