You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift静态库中停止BLE蓝牙扫描出现异常的问题求助

问题原因及解决方法

核心原因

  • CBCentralManager未初始化:静态库中的BluetoothViewController仅声明了centralManager变量,但未在任何初始化流程中创建实例。直接在APP内使用时,ViewController可能通过UIKit生命周期(如storyboard加载)间接完成必要初始化,但静态库中直接实例化时未触发该流程,导致centralManager为nil,调用stopScan()时抛出异常。
  • UIViewController与ObservableObject混用不合理:将UIViewController同时作为ObservableObject使用,静态库中直接实例化时未执行UIViewController的完整初始化逻辑,内部组件(如centralManager)无法正常初始化。
  • 空值安全缺失:调用centralManager.stopScan()时未做nil判断,一旦centralManager未初始化就会崩溃。

解决方法

方法1:重构BLE逻辑,剥离UIViewController依赖(推荐)

将BLE核心逻辑从UIViewController中抽离,单独实现独立的BLEManager类,不再依赖UIKit生命周期:

import CoreBluetooth
import Combine

class BLEManager: ObservableObject, CBCentralManagerDelegate, CBPeripheralDelegate {
    private var centralManager: CBCentralManager!
    var device: CBPeripheral?
    @Published var devicesFound: [CBPeripheral] = []
    var devicesDistance: [Int] = []
    
    // 初始化时创建CBCentralManager实例
    init() {
        centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.main)
    }
    
    // 检查蓝牙状态
    var isBluetoothOn: Bool {
        return centralManager.state == .poweredOn
    }
    
    func startScanning() {
        guard isBluetoothOn else { return }
        devicesFound.removeAll()
        devicesDistance.removeAll()
        centralManager.scanForPeripherals(withServices: [])
    }
    
    func stopScanning() {
        // 添加空值安全判断
        centralManager?.stopScan()
    }
    
    func connect(dev: CBPeripheral, uida: String) {
        centralManager?.connect(dev, options: nil)
    }
    
    // 实现蓝牙状态更新代理
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        // 可在此处理蓝牙状态变化(如提示用户开启蓝牙)
    }
    
    // 发现设备时收集信息
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        guard !devicesFound.contains(peripheral) else { return }
        devicesFound.append(peripheral)
        devicesDistance.append(RSSI.intValue)
    }
    
    // 实现其他必要代理方法(如连接成功、失败等)
}

项目中使用时直接实例化BLEManager:

import MYStaticLib

@ObservedObject private var bleManager = BLEManager()

DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
    bleManager.stopScanning()
    guard !bleManager.devicesFound.isEmpty,
          let minDistance = bleManager.devicesDistance.min(),
          let index = bleManager.devicesDistance.firstIndex(of: minDistance) else {
        return
    }
    print("Devices Found : \(bleManager.devicesFound.count)")
    print("Device With Minimum Distance : \(minDistance)")
    print("Device With Minimum Distance Index : \(index)")
    bleManager.device = bleManager.devicesFound[index]
    self.connect(dev: bleManager.device!, uida: uida)
}

方法2:修正原ViewController的初始化逻辑(保留ViewController场景)

如果必须保留BluetoothViewController,需确保其初始化流程中创建centralManager实例:

class BluetoothViewController: UIViewController, ObservableObject, CBCentralManagerDelegate, CBPeripheralDelegate {
    private var centralManager: CBCentralManager!
    var device: CBPeripheral?
    @Published var devicesFound : [CBPeripheral] = []
    var devicesDistance : [Int] = []
    
    // 重写初始化方法,确保centralManager被创建
    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        setupCentralManager()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupCentralManager()
    }
    
    private func setupCentralManager() {
        centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.main)
    }
    
    var isBluetoothOn: Bool {
        return centralManager?.state == .poweredOn ?? false
    }
    
    func startScanning () {
        guard isBluetoothOn else { return }
        devicesFound.removeAll()
        devicesDistance.removeAll()
        centralManager?.scanForPeripherals(withServices: [])
    }
    
    func stopScanning (){
        centralManager?.stopScan()
    }
    
    func connect(dev:CBPeripheral, uida : String){
        centralManager?.connect(dev,options: nil)
    }
    
    // 实现代理方法...
}

方法3:临时空值安全修复

在stopScanning方法中添加nil判断,避免崩溃(仅作为临时方案,不能解决根本问题):

func stopScanning (){
    centralManager?.stopScan()
}

内容的提问来源于stack exchange,提问作者Jawad Abbasi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 15:03:16