iOS Swift - 关于低功耗蓝牙管理器中信号量和排序的问题
iOS Swift - Question about semaphores and sequencing in Bluetooth Low Energy Manager
我以开源 Bluefruit 代码为例,特别是 BleManager class,它是与 CBCentralManager 的接口。我已经给作者发了邮件,他们没有回复:
看起来 class 是在单例架构中设计的(第 23 行的“共享”),因此在代码中的其他地方调用第一个“BleManager.connect() 时懒惰地构造.
让我感到困惑的是在 init() 中,有一个信号量“等待”函数:
override init() {
super.init()
centralManagerPoweredOnSemaphore.wait()
centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.global(qos: .background), options: [:])
// centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.main, options: [:])
}
我真的只习惯于 DispatchQueue.async() 线程中的信号量。这个 wait() 函数不是在主线程上调用的吗?这样不会封号吗?将取消阻止 .wait() 调用的 .signal() 调用位于第 289 行:
extension BleManager: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
DLog("centralManagerDidUpdateState: \(central.state.rawValue)")
// Unlock state lock if we have a known state
if central.state == .poweredOn || central.state == .poweredOff || central.state == .unsupported || central.state == .unauthorized {
centralManagerPoweredOnSemaphore.signal()
}
因此,一旦系统 centralManager 更新了 BLE 实用程序状态,就会调用它,只要它不是未知的,就会调用 .signal() 并且 init() 的其余部分将 运行 .我已经使用 print 语句来确认它是如何工作的。 centralManagerPoweredOnSemaphore.wait() 被调用,然后是 centralManagerDidUpdateState(),然后是 init() 的其余部分。不过我还是不明白:
- 主线程上的信号量 .wait() 怎么没有锁定所有内容?例如,查看第 169 和 170 行。那是做什么的?
- 委托尚未分配时,如何调用centralManagerDidUpdateState()?似乎委托是在 init() 的底部分配的,该函数在此函数之前没有立即调用。系统 CBCentralManager 如何知道使用特定的 centralManagerDidUpdateState() 函数?
- CBCentralManager 状态究竟是如何工作的?我已通读 official documentation but it's not too descriptive. It seems that when "Core Bluetooth initializes or resets" it will be "unknown"。然后在一段时间后,它是否会始终根据设备是否具有该功能以及用户 settings/permissions 是什么而更改为已知状态?所以我猜这就是为什么在我们得到响应之前阻止 class 的其余部分是“安全”的原因。但是在这种情况下是什么“初始化”了 Core Bluetooth?据我所知,在部分 init() 和 centralManagerDidUpdateState() 之前,没有调用任何核心蓝牙功能。是什么让 Core Bluetooth 尝试初始化 BLE 实用程序并更新状态?看起来这个 BleManager class 被延迟调用,所以它不可能是更高级别的东西。
虽然最后它起作用了,但我只是想知道事情的确切顺序实际上是如何 运行 使它正确地 运行 的。
他们正在使用信号量来阻止对 BleManager
对象的任何操作,直到中央退出 .unknown
状态。
创建的信号量初始值为 1。init
中的 wait
会将其递减为 0,并且不会阻塞。然后初始化中央,并在某个时候进入通电状态。此时signal
将return信号量设为1.
现在看看其他函数,例如 connect
函数(第 167 行),您会看到它们首先做的是 wait
,然后是 signal
信号量。考虑在两种不同状态下会发生什么:
- 中央尚未处于
.poweredOn
状态 - 信号量计数为 0,因此 wait
块。假设 BLE 状态在某个时候变为 .poweredOn
,wait
将结束,信号量立即释放,然后 connect
函数继续。
- central已经处于
.poweredOn
状态——信号量计数为1,所以wait
不阻塞,然后立即释放信号量,connect
函数继续.
其他函数的工作方式类似。
这种方法的优点是调用代码不需要一直检查状态;它可以访问 sharedInstance
然后立即调用 startScan
而无需检查 Central 是否已开机。
我以开源 Bluefruit 代码为例,特别是 BleManager class,它是与 CBCentralManager 的接口。我已经给作者发了邮件,他们没有回复:
看起来 class 是在单例架构中设计的(第 23 行的“共享”),因此在代码中的其他地方调用第一个“BleManager.connect() 时懒惰地构造.
让我感到困惑的是在 init() 中,有一个信号量“等待”函数:
override init() {
super.init()
centralManagerPoweredOnSemaphore.wait()
centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.global(qos: .background), options: [:])
// centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.main, options: [:])
}
我真的只习惯于 DispatchQueue.async() 线程中的信号量。这个 wait() 函数不是在主线程上调用的吗?这样不会封号吗?将取消阻止 .wait() 调用的 .signal() 调用位于第 289 行:
extension BleManager: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
DLog("centralManagerDidUpdateState: \(central.state.rawValue)")
// Unlock state lock if we have a known state
if central.state == .poweredOn || central.state == .poweredOff || central.state == .unsupported || central.state == .unauthorized {
centralManagerPoweredOnSemaphore.signal()
}
因此,一旦系统 centralManager 更新了 BLE 实用程序状态,就会调用它,只要它不是未知的,就会调用 .signal() 并且 init() 的其余部分将 运行 .我已经使用 print 语句来确认它是如何工作的。 centralManagerPoweredOnSemaphore.wait() 被调用,然后是 centralManagerDidUpdateState(),然后是 init() 的其余部分。不过我还是不明白:
- 主线程上的信号量 .wait() 怎么没有锁定所有内容?例如,查看第 169 和 170 行。那是做什么的?
- 委托尚未分配时,如何调用centralManagerDidUpdateState()?似乎委托是在 init() 的底部分配的,该函数在此函数之前没有立即调用。系统 CBCentralManager 如何知道使用特定的 centralManagerDidUpdateState() 函数?
- CBCentralManager 状态究竟是如何工作的?我已通读 official documentation but it's not too descriptive. It seems that when "Core Bluetooth initializes or resets" it will be "unknown"。然后在一段时间后,它是否会始终根据设备是否具有该功能以及用户 settings/permissions 是什么而更改为已知状态?所以我猜这就是为什么在我们得到响应之前阻止 class 的其余部分是“安全”的原因。但是在这种情况下是什么“初始化”了 Core Bluetooth?据我所知,在部分 init() 和 centralManagerDidUpdateState() 之前,没有调用任何核心蓝牙功能。是什么让 Core Bluetooth 尝试初始化 BLE 实用程序并更新状态?看起来这个 BleManager class 被延迟调用,所以它不可能是更高级别的东西。
虽然最后它起作用了,但我只是想知道事情的确切顺序实际上是如何 运行 使它正确地 运行 的。
他们正在使用信号量来阻止对 BleManager
对象的任何操作,直到中央退出 .unknown
状态。
创建的信号量初始值为 1。init
中的 wait
会将其递减为 0,并且不会阻塞。然后初始化中央,并在某个时候进入通电状态。此时signal
将return信号量设为1.
现在看看其他函数,例如 connect
函数(第 167 行),您会看到它们首先做的是 wait
,然后是 signal
信号量。考虑在两种不同状态下会发生什么:
- 中央尚未处于
.poweredOn
状态 - 信号量计数为 0,因此wait
块。假设 BLE 状态在某个时候变为.poweredOn
,wait
将结束,信号量立即释放,然后connect
函数继续。 - central已经处于
.poweredOn
状态——信号量计数为1,所以wait
不阻塞,然后立即释放信号量,connect
函数继续.
其他函数的工作方式类似。
这种方法的优点是调用代码不需要一直检查状态;它可以访问 sharedInstance
然后立即调用 startScan
而无需检查 Central 是否已开机。