等待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特性,功能,各种时间服务等进行多次读取时,它开始变得非常混乱。所以我想知道什么是等待委托的正确方法?在继续前进之前得到信号?回想一下,我不知道这些设备可能具有什么服务或特征。

解决方法

首先,如果您已经具有CountDownLatch for Android的实现,则可以仅对iOS执行相同的实现。是的,Swift没有内置的CountDownLatch,但是来自Uber created a good implementation的好朋友。

另一个选择是像您一样依赖变量,但是将其设为原子。在线有多种实现方式,其中包括同一Uber库中的一种。另一个示例在RxSwift library中。还有许多其他变体。原子变量将为变量的读/写操作提供线程安全性。

但最快捷的方法可能是拥有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将使用您提供的代码块执行。

,

每次致电didDiscoverCharacteristicsFor时,您只会收到一次致电device.discoverCharacteristics的电话。也就是说,仅在发现所有服务特征之后才进行didDiscoverCharacteristicsFor调用。

您已经将peripheral传递给了委托调用,因此您具有了解委托调用上下文所需的信息。无需“等待”。如果同时发现多个外围设备/服务的数据,则可以为每个外围设备或服务使用一个简单的状态机。

如果发现完成后需要采取一些措施,您甚至可以保留一组未处于完全发现状态的外围设备。例如一旦完成对外围设备的发现,便可以从该设备中删除该外围设备。如果该集合为空,则对所有设备的发现已完成。

所有此状态都属于您的模型。您的委托实现所需要做的就是使用已发现的数据更新模型。