首页 > 解决方案 > 等待 iOS Swift CBPeripheralDelegate 完成的正确方法?

问题描述

在进行蓝牙通信时,通常会遇到这样一种情况,即进行调用并在委托中获得响应,例如如下所示的特征发现:

func discoverCharacteristics(device: CBPeripheral)
{
    servicesCount = device.services!.count

    for service in device.services!
    {
        print("Discovering characteristics for service \(service.uuid)")
        device.discoverCharacteristics([], for: service)
    }
}

现在,此发现不是针对特定设备,而是针对遵循蓝牙 SIG 服务/配置文件的健康设备,因此我不确切知道它们可能具有哪些服务,也不知道每个服务中可能有多少特征。该方法是异步的,答案在以下委托方法中发出信号:

// Discovered Characteristics event
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?)
{
    for characteristic in service.characteristics!
    {
        print("Found characteristic \(characteristic.uuid)")
    }
    servicesCount = servicesCount - 1;
    print("Characteristics sets left: \(servicesCount)")
    if servicesCount == 0
    {
        print ("Found all characteristics")
        DispatchQueue.main.async{
            self.btleManager!.btleManagerDelegate.statusEvent(device: peripheral, statusEvent: Btle.CHARACTERISTICS_DISCOVERED)
        }
        self.device = peripheral
        self.handleMds()
    }
}

现在我需要等到发现完成后才能进行下一步,因为我下一步做什么通常取决于我得到了什么。在 Java 和 Android 中,我所做的是在调用方法中等待 CountDownLatch,然后我在回调中向锁存器发出信号以释放该等待。

CountDownLatch 的 iOS 等效项似乎是 DispatchSemaphore。但是,这样做显然会阻塞系统,并且不会调用任何委托。所以我所做的(如上面的代码所示)是servicesCount用服务数量初始化一个变量,并在每次发出信号时在委托回调中递减它。当它变为零时,我就完成了,然后我进行下一步。

这种方法有效,但看起来很老套;它不可能是正确的。当我需要对 DIS 特性、功能、各种时间服务等进行多次读取时,它开始变得非常混乱。 所以我想知道什么是等待代表发出信号的正确方法在前进之前?回想一下,我不知道这些设备可能具有哪些服务或特性。

标签: iosswiftcore-bluetooth

解决方案


首先,如果您已经为 Android 实现了 CountDownLatch,您可以为 iOS 执行相同的实现。是的,Swift 没有内置的CountDownLatch,但是来自 Uber的好人创造了一个很好的实现

另一种选择是依赖一个变量,就像你做的那样,但是让它成为原子的。网上有多种实现,包括同一个 Uber 库中的一种。另一个例子是在RxSwift 库中。还有许多其他变体。原子变量将为变量的读/写操作提供线程安全。

但可能最快捷的方法是拥有一个 DispatchGroup。看起来像这样:

let dispatchGroup = DispatchGroup() // instance-level definition

// ...

func discoverCharacteristics
{
    for service in device.services!
    {
        dispatchGroup.enter() 
        // ...
    }

    dispatchGroup.notify(queue: .main) {

        // All done
        print ("Found all characteristics")
        DispatchQueue.main.async{
            self.btleManager!.btleManagerDelegate.statusEvent(device: peripheral, statusEvent: Btle.CHARACTERISTICS_DISCOVERED)
        }
        self.device = peripheral
        self.handleMds()
    }

// ...

func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?)
{
    // ...
    dispatchGroup.leave()
}

换句话说,您在即将提交请求时进入组,在处理请求时离开。当所有项目离开组时,notify将使用您提供的块执行。


推荐阅读