iOS 11 CoreBluetooth:无法删除关键路径的观察者 CBPeripheral "delegate"

iOS 11 CoreBluetooth: Cannot remove an observer CBPeripheral for the key path "delegate"

自从 iOS 11 发布以来,我遇到了一次偶发但频繁的崩溃,具有以下特征:

Cannot remove an observer <CBPeripheral 0x1c010ef10> for the key path "delegate" from <CBPeripheral 0x1c010ef10> because it is not registered as an observer.

这发生在扫描蓝牙设备、稍后连接到其中一个设备以及整个过程的最终清理的上下文中。所有这些任务都在非主调度队列中执行,以减轻主线程的压力(为了更流畅的 UI 体验)。自从 iOS 9 天以来,这段代码一直 运行 没有发生任何事件,直到现在 iOS 11 出来,才开始崩溃。

到目前为止,我在网上找到的关于此行为的唯一参考文献是 this and this post for the Estimote SDK. These references suggest that something might be going on with parallel instances of the CBCentralManager in different dispatch queues, however, nothing regarding special care on the matter is stated in the official Programming Guide. Also, a response from an Apple Staff member to another CoreBluetooth 问题说明:

iOS 11 is in general going to be less forgiving for apps which don't hold a proper reference to CB objects...

听起来不是很鼓舞人心。我尝试分析应用程序并使用 XCode 及其配套工具寻找潜在的泄漏,但这也没有说明太多。

有没有其他人遇到过类似的问题?关于如何解决它的任何建议?关于下一步在哪里挖掘的想法?

经过一段时间的测试,在我们的特定情况下,解决方案包括将所有蓝牙堆栈相关工作转换为 mainQueue。这意味着所有相关的回调都存在于主线程范围内。

此解决方案需要格外小心在这些回调中执行的工作(UI 也在此处运行),但由于默认情况下大多数 CoreBluetooth 操作都是异步的,因此这证明可行。此解决方法已在 iOS 11 中得到确认,并且到目前为止,在 iOS 12 中也没有报告任何问题。

这里的要点是:只处理 mainQueue 中绝对必要的位,以及然后在必要时将剩余的负载转移到其他地方。