一、BLE 基础概念(必须懂)

1. BLE 是什么

Bluetooth Low Energy 低功耗蓝牙,特点:

  • 低功耗、连接快、小数据传输

  • 适用于:智能硬件、手环、车机、传感器、设备诊断

2. BLE 角色

  • Central(中心设备):手机(主动扫描、连接)

  • Peripheral(外设):硬件设备(被动广播、被连接)

3. BLE 通信核心结构

外设 → 服务 (Service) → 特征 (Characteristic)

  • Service:功能模块(UUID)

  • Characteristic:数据通道(读写 / 通知)

  • 所有数据交互都通过 Characteristic 完成


二、iOS BLE 开发完整流程(Swift 标准 7 步)

1. 初始化 CBCentralManager

import CoreBluetooth

class BLEManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
    var centralManager: CBCentralManager!
    var connectedPeripheral: CBPeripheral!
    
    override init() {
        super.init()
        centralManager = CBCentralManager(delegate: self, queue: .main)
    }
}

// 蓝牙状态回调
func centralManagerDidUpdateState(_ central: CBCentralManager) {
    if central.state == .poweredOn {
        print("蓝牙已开启,可以扫描")
    } else {
        print("蓝牙不可用")
    }
}

2. 扫描外设

// 开始扫描
func startScan() {
    guard centralManager.state == .poweredOn else { return }
    // 传入 nil 扫描所有设备,传入服务 UUID 可精准扫描
    centralManager.scanForPeripherals(withServices: nil, options: nil)
}

// 扫描到设备
func centralManager(_ central: CBCentralManager, 
                   didDiscover peripheral: CBPeripheral, 
                   advertisementData: [String : Any], 
                   rssi RSSI: NSNumber) {
    print("发现设备:\(peripheral.name ?? "未知设备")")
    // 记录设备、停止扫描、准备连接
}

// 停止扫描
func stopScan() {
    centralManager.stopScan()
}

3. 连接外设

func connect(peripheral: CBPeripheral) {
    connectedPeripheral = peripheral
    connectedPeripheral.delegate = self
    centralManager.connect(peripheral, options: nil)
}

// 连接成功
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
    print("连接成功")
    // 连接后自动搜索服务
    peripheral.discoverServices(nil)
}

// 连接失败
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
    print("连接失败:\(error?.localizedDescription ?? "")")
}

4. 发现服务

func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
    guard let services = peripheral.services else { return }
    for service in services {
        // 发现特征
        peripheral.discoverCharacteristics(nil, for: service)
    }
}

5. 发现特征

func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
    guard let characteristics = service.characteristics else { return }
    for charac in characteristics {
        print("特征UUID:\(charac.uuid)")
        // 可根据 UUID 匹配读写/通知特征
    }
}

6. 数据交互(核心)

① 读特征

func readCharacteristic(_ charac: CBCharacteristic) {
    connectedPeripheral.readValue(for: charac)
}

// 读结果回调
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
    if let data = characteristic.value {
        let value = String(data: data, encoding: .utf8)
        print("读取到数据:\(value ?? "")")
    }
}

② 写特征

func writeData(_ data: Data, to charac: CBCharacteristic) {
    connectedPeripheral.writeValue(data, for: charac, type: .withResponse)
}

③ 订阅通知(硬件主动推送)

func subscribeNotify(_ charac: CBCharacteristic) {
    connectedPeripheral.setNotifyValue(true, for: charac)
}

// 取消订阅
func unsubscribeNotify(_ charac: CBCharacteristic) {
    connectedPeripheral.setNotifyValue(false, for: charac)
}

// 通知数据回调(硬件主动发数据)
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
    if let data = characteristic.value {
        // 处理硬件推送的数据
    }
}

7. 断开连接

func disconnect() {
    if let peripheral = connectedPeripheral {
        centralManager.cancelPeripheralConnection(peripheral)
    }
}

// 断开回调
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
    print("设备断开连接")
    // 可在此处执行重连逻辑
}

三、GATT 机制详解(面试必问)

GATT = Generic Attribute Profile

BLE 数据交互的标准协议

规则:

  • Service 为功能单位

  • Characteristic 为数据单位

  • 手机和硬件通过 读写 / 通知 交互数据

Characteristic 三种交互方式

  1. Read:手机主动读

  2. Write:手机主动写

  3. Notify:硬件主动推送给手机(最常用)


四、断连重连机制(Swift 最稳定实现)

1. 监听断开(上面已实现)

2. 重连策略(直接用 UUID 重连,无需扫描)

// 存储设备 UUID
let storedUUIDString = "设备的identifier.uuidString"

// 重连方法
func reconnect() {
    guard let uuid = UUID(uuidString: storedUUIDString) else { return }
    let peripherals = centralManager.retrievePeripherals(withIdentifiers: [uuid])
    if let peripheral = peripherals.first {
        connectedPeripheral = peripheral
        connectedPeripheral.delegate = self
        centralManager.connect(peripheral, options: nil)
    }
}

3. 重连失败 → 延迟重试 / 重新扫描


五、iOS 对 BLE 的强限制(非常重要!)

1. 扫描限制

  • 后台扫描必须指定 Service UUID

  • 后台扫描频率降低

  • 不能无限扫描

2. 连接限制

  • 最多同时连接 ~20 个设备(看系统)

  • 连接失败不会自动重连

3. 后台运行限制

  • 必须开启 Background Modes → Uses Bluetooth LE accessories

  • 后台不能长时间扫描

  • 后台写数据可能延迟 / 被挂起

4. 权限限制

  • iOS 13+:NSBluetoothAlwaysUsageDescription

  • iOS 14+:NSBluetoothPeripheralUsageDescription

  • 不配置权限直接崩溃

5. 电池优化限制

  • 系统会自动休眠 BLE

  • 频繁扫描会导致耗电飙升

  • 不用时必须停止扫描

6. 系统杀死 APP

  • 后台超过一定时间会被系统 kill

  • 被杀死后 无法自动重连(除非重新打开 APP)


六、iOS BLE 开发注意事项(Swift 避坑大全)

1. 必须判断 CBCentralManager 状态

状态未启用就扫描 → 直接无效

2. 不要连续调用 connect

  • 连接中再次 connect → 系统报错

  • isConnecting 标记位

3. 订阅通知前必须判断特征属性

if charac.properties.contains(.notify) {
    // 允许订阅
}

4. 数据分包发送

BLE 单次最大传输 20 字节
超过必须分包、组包

5. 不要阻塞主线程

所有 BLE 回调都在子线程
UI 更新必须切主线程

DispatchQueue.main.async {
    // 更新 UI
}

6. 多设备管理

用 UUID 作为唯一标识
不要用外设对象 / 名称

7. 处理设备信号弱

  • 信号弱(RSSI < -80)会导致卡顿、断连

  • 不要远距离操作

8. 释放逻辑

  • 页面退出时:

    • 停止扫描

    • 取消连接

    • 置空代理
      防止内存泄漏、崩溃


七、面试高频 BLE 问题总结

  1. BLE 经典蓝牙区别?

  2. BLE GATT 结构?

  3. Characteristic 三种交互方式?

  4. iOS 后台 BLE 限制?

  5. 断连重连如何实现?

  6. BLE 数据超过 20 字节怎么办?

  7. iOS 13+ 蓝牙权限?

  8. BLE 开发常见崩溃原因?


八、一句话总结(面试背诵)

iOS BLE 开发基于 GATT 协议,流程为:
初始化 → 扫描 → 连接 → 发现服务 → 发现特征 → 读写 / 通知 → 断开 / 重连
iOS 对扫描、后台、权限、电量都有严格限制,开发必须注意线程、分包、重连、内存、权限、后台模式,才能保证稳定不崩溃。

更多推荐