каналы core.async в iOS Swift для разделения компонентов

Как мне отделить компоненты в Swift от каналов или эквивалентной реализации шины сообщений?

Как новичок в Swift, перешедший из Clojure, я привык возвращать канал core.async при запуске компонента, а затем подключать его к вызывающей стороне для управления потоком.

Я вижу, что в Swift есть что-то под названием DispatchQueue, но это не так. t выглядит как шина сообщений и, похоже, не имеет буферизации.

В частности, я управляю аудиоподсистемами на iOS, и мне нужно отправлять ленивые сигналы через подключаемую архитектуру.


person Petrus Theron    schedule 25.07.2018    source источник


Ответы (1)


Я нашел простую реализацию шины событий для DispatchQueue, который объединяет методы 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))
person Petrus Theron    schedule 30.07.2018
comment
Проблема, с которой я сталкиваюсь при использовании этого подхода, заключается в том, что вызов широковещательной рассылки во время блокировки подписки приводит к фатальной ошибке. Почему? - person Petrus Theron; 15.08.2018