You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将Core Bluetooth回调封装为Async/Await并解决多读取同步问题

解决Core Bluetooth多次async/await读取特征值的同步问题

你的问题出在只用了单个readContinuation变量存储延续对象——当连续发起多次读取请求时,新的请求会覆盖之前的延续,导致前一次读取的回调无法正确唤醒对应的await,最终出现顺序混乱的问题。

解决方案:用特征映射管理延续对象

通过字典将每个读取任务的延续对象与对应的特征绑定,避免覆盖问题,同时确保每个回调能精准唤醒对应的异步任务。

1. 定义类型别名与延续字典

在你的Bluetooth代理类中,先定义延续类型的别名,并维护一个字典来存储特征与延续的映射:

typealias ReadCharacteristicContinuation = CheckedContinuation<CBCharacteristic, Error>

// 存储每个特征对应的读取延续对象
private var readContinuations: [CBCharacteristic: ReadCharacteristicContinuation] = [:]

2. 实现async/await版的读取方法

重写你的读取方法,将延续对象存入字典后再发起读取请求:

func read(for characteristic: CBCharacteristic) async throws -> [UInt8] {
    return try await withCheckedThrowingContinuation { continuation in
        // 检查当前特征是否已有未完成的读取请求,避免重复发起
        guard readContinuations[characteristic] == nil else {
            let error = NSError(domain: "BluetoothError", code: -1, userInfo: [
                NSLocalizedDescriptionKey: "当前特征存在未完成的读取请求"
            ])
            continuation.resume(throwing: error)
            return
        }
        
        readContinuations[characteristic] = continuation
        characteristic.peripheral?.readValue(for: characteristic)
    }.value ?? []
}

3. 调整代理回调逻辑

在didUpdateValueFor回调中,根据当前特征取出对应的延续对象,完成唤醒后从字典中移除:

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
    // 取出并移除当前特征对应的延续对象
    guard let continuation = readContinuations.removeValue(forKey: characteristic) else {
        return
    }
    
    if let error = error {
        continuation.resume(throwing: error)
    } else {
        continuation.resume(returning: characteristic)
    }
}

4. 顺序读取的使用示例

现在你可以直接按顺序调用read(for:),前一个读取完成后才会执行下一个:

func readMultipleFeatures() async throws {
    guard let charA = peripheral?.characteristics?.first(where: { $0.uuid == charAUuid }),
          let charB = peripheral?.characteristics?.first(where: { $0.uuid == charBUuid }) else {
        return
    }
    
    // 先读取特征A,完成后再读取特征B
    let valueA = try await read(for: charA)
    print("特征A的值:\(valueA)")
    
    let valueB = try await read(for: charB)
    print("特征B的值:\(valueB)")
}

额外注意事项

  • 字典使用CBCharacteristic作为键是安全的,系统已为其实现了正确的哈希与相等性判断
  • 如果需要并发读取不同特征,该方案也支持——不同特征的延续对象存在字典的不同键下,互不干扰
  • 加入重复请求检查可以避免同一特征被多次发起读取导致的回调混乱

内容的提问来源于stack exchange,提问作者geri-m

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 18:54:57