iOS Swift中用于分离组件的core.async通道

时间:2018-07-25 15:27:20

标签: ios swift clojure core.async

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

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

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

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

1 个答案:

答案 0 :(得分:0)

我发现DispatchQueue中的a simple event bus implementation封装了notifyasync方法,并具有一定的锁定功能,以防止多个订阅者在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))