CoreBluetooth iOS 如果使用 WriteWithoutResponse 时可能发生数据竞争

CoreBluetooth iOS if data race possible when using WriteWithoutResponse

我想用我的外围设备发送一个字节流class。所以我在问自己是否需要线程安全队列,或者删除它以解锁流程是否安全。

我有以下结构

private struct WriteWithoutResponseContext {
    let data: Data
    let characteristic: Characteristic
} 

还有这两个属性

private var writeWithoutResponseContextQueue = Queue<WriteWithoutResponseContext>()
private let writeWithoutResponseContextCachingDispatchQueue = DispatchQueue(label: "thread-safe-unsent-data-caching", attributes: .concurrent)

我正在使用带有屏障的队列来确保所有内容都被阻塞,直到我在 canSendWriteWithoutResponse 为 false 时将其添加到队列中。

我们怎么知道在检查 canSendWriteWithoutResponse 和上下文排队之间没有发生 peripheralIsReady?

   func writeValueWithoutResponse(_ data: Data, for characteristic: Characteristic) {
        if self.cbPeripheral.canSendWriteWithoutResponse {
            self.cbPeripheral.writeValue(data, for: characteristic, type: .withoutResponse)
        } else {
            self.writeWithoutResponseContextCachingDispatchQueue.async(flags: .barrier) {
                let context = WriteWithoutResponseContext(data: data, characteristic: characteristic)
                self.writeWithoutResponseContextQueue.enqueue(context)
            }
        }
    }

    func peripheralIsReady(toSendWriteWithoutResponse peripheral: CBPeripheral) {
        self.writeWithoutResponseContextCachingDispatchQueue.sync {
            if let unsentContext = self.writeWithoutResponseContextQueue.dequeue() {
                self.cbPeripheral.writeValue(unsentContext.data, for: unsentContext.characteristic, type: .withoutResponse)
            }
        }
    }

为了安全起见,您需要指定您的 CentralManger 正在运行的队列,例如,您的中央管理器在其中调度中央角色事件。 如果您没有指定任何内容,CentralManager 将使用主队列。

self.centralManager = CBCentralManager(delegate: self, queue: nil, options: options)

所以在我的例子中,只需创建带有串行队列的 CentralManager 就可以了。

let serialQueue =  DispatchQueue(label: "com.Whosebug.64819549", qos: .userInitiated)
self.centralManager = CBCentralManager(delegate: self, queue: serialQueue, options: options)

有了它我就不需要屏障了