iOS 蓝牙开发是面试中经常考察的知识点,尤其是涉及硬件交互、IoT 等领域的岗位。下面我为你整理了常见的面试问题,分为基础概念 、核心流程 、进阶问题 和实战问题几大类。
一、基础概念
1. 蓝牙有几种技术标准?iOS 开发主要用哪种?
- 经典蓝牙 (Bluetooth Classic): 传输速率高、功耗大,主要用于音频传输、文件传输等场景。iOS 对经典蓝牙的设备间通信支持有限(主要通过 MFI 认证)。
- 低功耗蓝牙 (Bluetooth Low Energy, BLE) : 功耗低、数据量小,主要用于设备状态同步、传感器数据采集等。iOS 蓝牙开发主要围绕 BLE。
2. iOS 中处理蓝牙的核心框架是哪个?
- CoreBluetooth.framework。
- 它提供了与 BLE 设备交互的所有必要类和方法。
3. 解释 BLE 中的中心设备 (Central) 和外设 (Peripheral) 角色
- 外设 (Peripheral): 广播数据的设备,例如智能手环、心率监测器。它持有数据。
- 中心设备 (Central): 扫描并连接外设的设备,例如 iPhone。它消费数据。
- 在 iOS 开发中,我们的 App 通常作为 Central 去连接其他 BLE 设备。 但 iOS 设备也可以作为 Peripheral(通过
CBPeripheralManager
)。
二、核心流程与 API
4. 描述一个完整的 Central 端连接和数据交互流程
这是最核心的问题,几乎必问。
-
创建中心管理器
swiftcentralManager = CBCentralManager(delegate: self, queue: nil)
-
监听蓝牙状态 (CBCentralManagerDelegate)
swiftfunc centralManagerDidUpdateState(_ central: CBCentralManager) { if central.state == .poweredOn { // 蓝牙已开启,开始扫描 central.scanForPeripherals(withServices: nil, options: nil) } }
-
发现外设
swiftfunc centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { // 检查外设名称或广播数据,找到目标设备 if peripheral.name == "MyDevice" { self.targetPeripheral = peripheral central.stopScan() central.connect(peripheral, options: nil) } }
-
连接外设
swiftfunc centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { // 连接成功,设置 Peripheral 的代理并开始发现服务 peripheral.delegate = self peripheral.discoverServices(nil) // 传入 nil 发现所有服务 }
-
发现服务 (CBPeripheralDelegate)
swiftfunc peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { if let services = peripheral.services { for service in services { // 根据目标服务的 UUID 进行过滤 if service.uuid == CBUUID(string: "180D") { // 心率服务 peripheral.discoverCharacteristics(nil, for: service) } } } }
-
发现特征
swiftfunc peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { if let characteristics = service.characteristics { for characteristic in characteristics { // 根据特征的属性进行操作 if characteristic.properties.contains(.notify) { peripheral.setNotifyValue(true, for: characteristic) } if characteristic.properties.contains(.read) { peripheral.readValue(for: characteristic) } // 如果需要写入 // if characteristic.properties.contains(.write) { ... } } } }
-
数据交互
- 读取数据 :
peripheral.readValue(for: characteristic)
然后在didUpdateValueFor
回调中获取数据。 - 订阅通知 :
peripheral.setNotifyValue(true, for: characteristic)
数据更新时,会在didUpdateValueFor
回调中收到。 - 写入数据 :
peripheral.writeValue(data, for: characteristic, type: .withResponse)
(或.withoutResponse
)
- 读取数据 :
-
断开连接
swiftcentralManager.cancelPeripheralConnection(peripheral)
5. 解释 BLE 的 GATT 结构
- GATT (Generic Attribute Profile): 定义了 BLE 设备间数据传输的格式和结构。
- 层级关系 : Device -> Service -> Characteristic -> Descriptor
- Service (服务): 一个设备提供的特定功能,例如电池服务、设备信息服务。用 UUID 标识。
- Characteristic (特征): 服务下的具体数据点,是数据交互的真正载体。例如电池电量、心率测量值。它有自己的 UUID、值和属性(读、写、通知等)。
- Descriptor (描述符): 对 Characteristic 的额外描述,例如用户描述、特征格式等。
6. CBCharacteristic
的 properties
有哪些?各自含义是什么?
.read
: 可读.write
: 可写(需要响应).writeWithoutResponse
: 可写(不需要响应,速度快但不可靠).notify
: 可订阅(外设主动推送数据,无确认).indicate
: 可指示(外设主动推送数据,有确认,更可靠)- 等等。在操作一个特征前,必须检查其属性。
三、进阶问题
7. 后台模式下的蓝牙处理
- 配置 : 在
Capabilities
中开启Background Modes
并勾选Uses Bluetooth LE accessories
。 - 限制 :
- 扫描时需要使用 Service UUIDs(
scanForPeripherals(withServices: [CBUUID], options: ...)
),否则在后台扫描不到设备。 - App 在后台或被挂起时,所有蓝牙事件都会在后台队列中被缓存,当 App 回到前台时会一并交付。
- 扫描时需要使用 Service UUIDs(
- 状态保存与恢复 : 通过实现
restoreIdentifier
和centralManager(_:willRestoreState:)
方法,可以让系统在 App 被杀死后重新启动时恢复蓝牙连接和状态。
8. 如何保证蓝牙连接的稳定性和重连机制?
- 监听断开事件 : 实现
centralManager(_:didDisconnectPeripheral:error:)
代理方法。 - 实现重连逻辑: 在断开回调中,根据错误码和业务逻辑进行重试连接。
- 连接超时处理 :
CoreBluetooth
没有原生超时,需要自己用DispatchWorkItem
或Timer
实现。 - 错误处理: 妥善处理常见的错误,如连接失败、服务发现失败等。
9. 蓝牙配对和绑定 (Bonding) 的区别?
- 配对 (Pairing): 一个一次性的过程,用于交换密钥、认证身份,建立安全的连接。
- 绑定 (Bonding): 在配对成功后,将交换的密钥(LTK, Long-Term Key)存储起来,以便后续重新连接时无需再次配对。在 iOS 上,这个过程由系统自动管理。
10. 作为 Peripheral 端 (CBPeripheralManager
) 需要做什么?
- 创建
CBPeripheralManager
。 - 设置服务和特征(
CBMutableService
和CBMutableCharacteristic
)。 - 发布服务到本地数据库。
- 开始广播。
- 处理 Central 的订阅、读请求和写请求。
四、实战与经验
11. 你在项目中遇到的蓝牙难点是什么?如何解决的?
这是一个开放性问题,考察实际经验。可能的答案:
- 连接不稳定: 实现了指数退避算法的重连机制。
- 数据分包与组包: BLE 单次传输数据量有限(通常是 20 字节),需要设计协议来处理长数据。
- 多设备连接管理 : 使用字典或数组管理多个
CBPeripheral
实例。 - 不同厂商设备兼容性: 处理非标准的 UUID 或不符合规范的 GATT 结构。
12. 如何调试蓝牙问题?
- 使用 LightBlue 、nRF Connect 等第三方 App 模拟 Peripheral 或扫描设备,验证硬件本身是否正常。
- 在 Xcode 中查看
CoreBluetooth
的日志(有时需要额外的系统日志工具)。 - 检查所有的 Delegate 回调,特别是错误回调。
- 使用
Packet Logger
(需要苹果开发者账号)进行底层 HCI 日志抓取,这是最强大的调试手段。
13. 了解 Bluetooth 5.0 的新特性吗?
- 2M PHY: 更高的传输速率。
- LE Audio: 新一代蓝牙音频标准。
- Long Range: 更长的传输距离。
- Advertising Extensions: 更强大的广播数据能力。
- 注意: 这些特性需要手机硬件和外围设备同时支持,并且 iOS API 可能对部分特性有版本要求。