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

CoreBluetooth调用discoverServices报错及连接异常问题求助

Hey there, let's work through these two CoreBluetooth API misuse issues you're hitting—they're common pitfalls, but easy to fix once you know the root causes!


1. First Error: discoverServices called on a disconnected peripheral

What's causing it?

When the didDiscover delegate method fires, the CBPeripheral object it gives you is only discovered via BLE advertising—it's not connected to your device yet. CoreBluetooth strictly requires peripherals to be in the connected state before you can call methods like discoverServices. Your original code skips the connection step entirely, which triggers the API misuse warning.

How to fix it

You need to follow CoreBluetooth's standard workflow:

  • First, initiate a connection to the discovered peripheral
  • Wait for the connection to be confirmed via the didConnect delegate method
  • Only then call discoverServices

2. Second Error: Cancelling connection for unused peripheral

What's causing it?

In your second code snippet, the CBPeripheral instance is a local variable inside the didDiscover method. As soon as that method finishes executing, ARC (Automatic Reference Counting) deallocates the peripheral because there's no strong reference keeping it alive. CoreBluetooth detects this and cancels the connection, since the peripheral is no longer "active" in your app.

How to fix it

Store a class-level strong reference to the peripheral (e.g., a property in your Bluetooth manager class). This keeps the object alive while the connection process and subsequent BLE operations are running.


Full Fixed Code Example

Here's a complete, corrected implementation that addresses both issues:

class BluetoothManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
    // CoreBluetooth objects
    private var centralManager: CBCentralManager!
    private var connectedPeripheral: CBPeripheral? // Strong reference to keep peripheral alive
    
    // Your custom properties
    private var peripherals: [Peripheral] = []
    private let targetServiceUUID = CBUUID(string: "4fafc201-1fb5-459e-8fcc-c5c9c331914b")
    private let acceptedServices = ["4fafc201-1fb5-459e-8fcc-c5c9c331914b"]

    override init() {
        super.init()
        centralManager = CBCentralManager(delegate: self, queue: nil)
    }

    // MARK: - CBCentralManagerDelegate
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        if central.state == .poweredOn {
            // Start scanning for peripherals advertising our target service
            central.scanForPeripherals(withServices: [targetServiceUUID], options: nil)
        }
    }

    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        let peripheralName = advertisementData[CBAdvertisementDataLocalNameKey] as? String ?? "Unknown"
        
        // Verify the peripheral advertises our accepted service
        guard let serviceUUIDs = advertisementData[CBAdvertisementDataServiceUUIDsKey] as? [CBUUID],
              serviceUUIDs.contains(where: { acceptedServices.contains($0.uuidString) }) else {
            return
        }

        let newPeripheral = Peripheral(id: peripherals.count, name: peripheralName, rssi: RSSI.intValue)
        print("Discovered valid peripheral: \(newPeripheral)")

        // Stop scanning to avoid duplicate discoveries
        central.stopScan()

        // Store strong reference and set delegate before connecting
        connectedPeripheral = peripheral
        peripheral.delegate = self

        // Initiate connection
        central.connect(peripheral, options: nil)
    }

    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        print("Successfully connected to \(peripheral.name ?? "Unknown")")
        // Now we can safely discover services
        peripheral.discoverServices([targetServiceUUID])
    }

    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
        print("Connection failed: \(error?.localizedDescription ?? "Unknown error")")
        connectedPeripheral = nil // Clear reference on failure
        // Restart scanning if needed
        central.scanForPeripherals(withServices: [targetServiceUUID], options: nil)
    }

    // MARK: - CBPeripheralDelegate
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        if let error = error {
            print("Service discovery failed: \(error.localizedDescription)")
            return
        }

        guard let services = peripheral.services else { return }
        for service in services {
            print("Discovered service: \(service.uuid)")
            // Optional: Discover characteristics for the service
            peripheral.discoverCharacteristics(nil, for: service)
        }
    }
}

// Your custom Peripheral model
struct Peripheral {
    let id: Int
    let name: String
    let rssi: Int
}

Key Takeaways

  • Always follow CoreBluetooth's sequence: Scan → Discover → Connect → Discover Services → Discover Characteristics
  • Never call service/characteristic discovery methods on a disconnected peripheral
  • Maintain a strong reference to any CBPeripheral you're interacting with to prevent ARC from deallocating it prematurely

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:03:44