【问题标题】:core.async channels in iOS Swift for decoupling componentsiOS Swift 中用于解耦组件的 core.async 通道
【发布时间】:2018-07-25 15:27:20
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: ios swift clojure core.async


    【解决方案1】:

    我发现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))
    

    【讨论】:

    • 我在使用这种方法时遇到的一个问题是,在订阅块期间调用广播似乎会引发致命错误。为什么?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-11
    • 1970-01-01
    • 2015-10-29
    • 2014-03-20
    • 1970-01-01
    • 2015-07-04
    相关资源
    最近更新 更多