iOS 蓝牙开发进阶:彻底理解 CBManager(状态、权限与正确使用方式)

在 iOS 蓝牙开发中,很多人把精力集中在 CBCentralManagerCBPeripheralManager 上,但真正决定蓝牙是否"能正常工作"的,其实是它们的父类:CBManager

如果对 CBManager 理解不透,通常会出现这些问题:

  • 扫描时机不对(经常扫不到设备)
  • 权限处理混乱
  • 蓝牙开关变化后逻辑异常
  • SDK 封装设计不合理

这篇文章从设计本质 + 状态模型 + 实战用法 + SDK 封装 四个层面,彻底讲清楚 CBManager


一、CBManager 的本质

先看继承关系:

CBManager

├── CBCentralManager

└── CBPeripheralManager

👉 结论:

CBManager 是 CoreBluetooth 中所有蓝牙管理器的统一状态抽象层。

它本身不能被实例化:

objectivec 复制代码
- (instancetype)init NS_UNAVAILABLE;

它只负责两件事:

  1. 当前蓝牙系统状态(state)

  2. 当前应用权限状态(authorization)


二、CBManagerState:蓝牙"能不能用"的唯一依据

objectivec 复制代码
typedef NS_ENUM(NSInteger, CBManagerState) {
    CBManagerStateUnknown = 0,
    CBManagerStateResetting,
    CBManagerStateUnsupported,
    CBManagerStateUnauthorized,
    CBManagerStatePoweredOff,
    CBManagerStatePoweredOn,
};

这是整个 CoreBluetooth 的核心状态机


1. Unknown(未知状态)

  • 初始化后默认状态
  • 很快会进入其他状态

👉 此时不能做任何蓝牙操作


2. Resetting(系统重置中)

  • 蓝牙服务短暂不可用
  • CoreBluetooth 正在恢复

👉 实战建议:

暂停业务,等待状态恢复


3. Unsupported(设备不支持)

  • 当前设备不支持 BLE

👉 现代 iPhone 基本不会出现,但必须处理。


4. Unauthorized(没有权限)

  • App 没有蓝牙权限

👉 可能原因:

  • 用户拒绝
  • 系统限制(如家长控制)

5. PoweredOff(蓝牙关闭)

  • 系统蓝牙开关关闭

👉 和 Unauthorized 区别:

状态 含义
Unauthorized 没权限
PoweredOff 蓝牙没开

6. PoweredOn(唯一可用状态)

👉 只有这个状态下,才能执行所有蓝牙操作:

objectivec 复制代码
scan / connect / discover / write / notify / advertise

三、CBManagerAuthorization:权限模型

objectivec 复制代码
typedef NS_ENUM(NSInteger, CBManagerAuthorization) {
    CBManagerAuthorizationNotDetermined = 0,
    CBManagerAuthorizationRestricted,
    CBManagerAuthorizationDenied,
    CBManagerAuthorizationAllowedAlways
};

状态含义

状态 含义
NotDetermined 用户尚未选择
Restricted 系统限制
Denied 用户拒绝
AllowedAlways 已授权

⚠️ 关键认知

authorization ≠ 蓝牙是否可用

例如:

AllowedAlways + PoweredOff → 仍然不能用


四、正确使用流程(核心重点)

很多人的错误流程:

先判断权限 → 再创建 manager → 再做业务

❌ 错误。


✅ 正确流程

  1. 创建 manager

  2. 等待状态回调

  3. state == PoweredOn → 开始业务


标准代码

objectivec 复制代码
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {

    switch (central.state) {

        case CBManagerStatePoweredOn:
            [self startScan];
            break;

        case CBManagerStatePoweredOff:
            // 提示用户打开蓝牙
            break;

        case CBManagerStateUnauthorized:
            // 提示权限问题
            break;

        case CBManagerStateResetting:
            // 暂停业务
            break;

        default:
            break;
    }
}

一句话总结

是否能做蓝牙业务,只看:

state == CBManagerStatePoweredOn


五、为什么必须等 PoweredOn?

因为:

state 是"最终裁决者"

它综合了:

  • 权限状态
  • 蓝牙开关状态
  • 系统服务状态

六、authorization 的正确用法

它是辅助信息,而不是流程控制核心。


用法 1:优化用户体验

objectivec 复制代码
if (@available(iOS 13.1, *)) {
    if (CBManager.authorization == CBManagerAuthorizationDenied) {
        // 提前提示用户
    }
}

用法 2:区分 Unauthorized 原因

objectivec 复制代码
if (central.state == CBManagerStateUnauthorized) {
    if (@available(iOS 13.1, *)) {
        if (CBManager.authorization == CBManagerAuthorizationDenied) {
            // 用户拒绝
        }
    }
}

七、SDK 封装设计(实战重点)

如果你在做蓝牙 SDK,这一节非常关键。


❌ 不推荐设计

objectivec 复制代码
@property (nonatomic, assign, readonly) CBManagerState state;

问题:

  • 暴露系统实现细节
  • 上层需要理解 CoreBluetooth
  • 不利于扩展

✅ 推荐设计

1. 自定义状态

objectivec 复制代码
typedef NS_ENUM(NSInteger, BluetoothState) {
    BluetoothStateUnknown = 0,
    BluetoothStateResetting,
    BluetoothStateUnsupported,
    BluetoothStateUnauthorized,
    BluetoothStatePoweredOff,
    BluetoothStatePoweredOn,
};

  1. 提供只读属性
objectivec 复制代码
@property (nonatomic, assign, readonly) BluetoothState bluetoothState;

  1. 提供状态回调(必须)
objectivec 复制代码
@protocol BluetoothManagerDelegate <NSObject>

@optional
- (void)bluetoothManagerDidUpdateState:(BluetoothState)state;

@end

为什么必须要回调?

因为状态是动态变化的:

Unknown → PoweredOn

PoweredOn → PoweredOff

PoweredOff → PoweredOn

如果只有属性:

objectivec 复制代码
manager.bluetoothState

👉 上层无法感知变化。


标准实现

objectivec 复制代码
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {

    BluetoothState newState = [self mapState:central.state];

    _bluetoothState = newState;

    if ([self.delegate respondsToSelector:@selector(bluetoothManagerDidUpdateState:)]) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.delegate bluetoothManagerDidUpdateState:newState];
        });
    }
}

八、常见错误总结


❌ 初始化后立即扫描

objectivec 复制代码
[self.central scanForPeripheralsWithServices:nil options:nil];

👉 错误(state 未 ready)


❌ 只判断权限

objectivec 复制代码
if (CBManager.authorization == AllowedAlways)

👉 不够,必须看 state


❌ 忽略 Resetting

👉 会导致"偶现问题"


❌ 只暴露属性,没有回调

👉 SDK 设计不完整


九、总结

你只需要记住三点:


1️⃣ CBManager 是蓝牙状态抽象层

统一管理:state + authorization


2️⃣ 唯一业务入口

state == CBManagerStatePoweredOn


3️⃣ SDK 设计原则

属性:当前状态

回调:状态变化


相关推荐
文件夹__iOS17 小时前
SwiftUI 核心选型:class + ObservableObject VS struct + @State
ios·swiftui·swift
SameX1 天前
独立开发了一款健康记录 App,聊聊几个让我纠结很久的设计决策
ios
报错小能手1 天前
Swift UI 框架 实战 简易计数器、待办清单 、随机壁纸图库、个人笔记
ui·ios
游戏开发爱好者81 天前
深入理解iOSTime Profiler:提升iOS应用性能的关键工具
android·ios·小程序·https·uni-app·iphone·webview
for_ever_love__2 天前
UI学习:多界面传值的正向传值(属性传值)和反向传值(代理传值)
学习·ui·ios·objective-c
开心就好20252 天前
全面介绍iOS开发工具:Xcode、AppCode、CocoaPods、Fastlane和Git
后端·ios
懋学的前端攻城狮2 天前
数据持久化与缓存策略:在离线与在线间架起桥梁
ios·swift
~央千澈~2 天前
以cocos3.8.8开发的游戏为例商业实战项目举例cocos打包ios苹果安装包ipa完整详细教程-优雅草卓伊凡
ios
SameX2 天前
iOS 足迹 App 的成就系统,我推倒重做了一次——踩了3个坑之后
ios