iOS同一APP中多CBCentralManager未调用状态更新函数问题
多个CBCentralManager实例仅第一个触发状态更新(iOS 16)
环境
- iOS版本:16.1/16.2
- Xcode版本:14.1
问题描述
初始化多个CBCentralManager实例时,仅第一个实例会调用代理的centralManagerDidUpdateState()方法更新状态,其余实例状态始终为unknown,不会触发状态更新回调。
期望结果
所有CBCentralManager实例都能调用各自代理的centralManagerDidUpdateState()方法。比如测试代码中的第二个管理器应打印SECOND MANAGER UPDATED STATE.。
背景
因引入第三方库需使用多个CBCentralManager,该库会自行初始化Core Bluetooth中心管理器。按库文档说明,相关对象应可在自定义centralManager代理的didDiscover方法内初始化,但实际运行异常,排查后确定问题出在多CBCentralManager的使用上。
目前临时方案是仅使用一个CBCentralManager实例传入该库,但需修改库源码,可能引发非官方兼容问题。该库文档显示2022年9月功能正常,不确定是Apple修改了Core Bluetooth行为限制多实例运行,还是开发环境存在问题。
复现代码
ModelData(初始化中心管理器)
import Foundation import Combine import CoreBluetooth final class ModelData { let centralManager: CBCentralManager! let secondCentralManager: CBCentralManager! init() { let centralManagerDelegate = CentralManagerDelegate() centralManager = CBCentralManager(delegate: centralManagerDelegate, queue: nil) let secondCMDelegate = SecondCentralManagerDelegate() secondCentralManager = CBCentralManager(delegate: secondCMDelegate, queue: nil) print("initialized central managers") } }
第一个中心管理器代理类
import Foundation import CoreBluetooth class CentralManagerDelegate: NSObject, ObservableObject, CBCentralManagerDelegate { func centralManagerDidUpdateState(_ central: CBCentralManager) { switch central.state { case .unknown: print("Bluetooth Device is UNKNOWN") case .unsupported: print("Bluetooth Device is UNSUPPORTED") case .unauthorized: print("Bluetooth Device is UNAUTHORIZED") case .resetting: print("Bluetooth Device is RESETTING") case .poweredOff: print("Bluetooth Device is POWERED OFF") case .poweredOn: print("Bluetooth Device is POWERED ON") @unknown default: print("Unknown State") } } }
第二个中心管理器代理类
import Foundation import CoreBluetooth class SecondCentralManagerDelegate: NSObject, ObservableObject, CBCentralManagerDelegate { func centralManagerDidUpdateState(_ central: CBCentralManager) { print("SECOND MANAGER UPDATED STATE") } }
调用ModelData的视图
struct HomeScreen: View { @StateObject private var modelData = ModelData() @State private var showDetail = true var body: some View { } }
运行输出
initialized central managers Bluetooth Device is POWERED ON
解决方案
1. 核心原因
CBCentralManager对代理的引用是弱引用,你代码中的代理对象是局部变量,初始化完成后无强引用持有者,被ARC提前释放,因此无法接收回调。
2. 修复代码
修改ModelData类,将代理对象保存为属性以保持强引用:
final class ModelData { let centralManager: CBCentralManager! let secondCentralManager: CBCentralManager! // 添加属性持有代理对象 private let centralDelegate: CentralManagerDelegate private let secondCentralDelegate: SecondCentralManagerDelegate init() { centralDelegate = CentralManagerDelegate() centralManager = CBCentralManager(delegate: centralDelegate, queue: nil) secondCentralDelegate = SecondCentralManagerDelegate() secondCentralManager = CBCentralManager(delegate: secondCentralDelegate, queue: nil) print("initialized central managers") } }
3. 额外说明
- Apple官方并未限制同时运行多个
CBCentralManager实例,只要每个实例的代理被正确持有,就能正常接收状态更新及其他回调。 - 若不想修改第三方库源码,可尝试使用全局单例
CBCentralManager配合库的外部管理器传入接口(若支持),既能避免多实例问题,也无需修改库代码。
内容的提问来源于stack exchange,提问作者Wake
相关产品推荐
相关产品推荐

