CoreBluetooth 功能在 Singleton 中不起作用

Posted

技术标签:

【中文标题】CoreBluetooth 功能在 Singleton 中不起作用【英文标题】:CoreBluetooth Functions not working from Singleton 【发布时间】:2018-08-09 15:56:09 【问题描述】:

所以我目前在 iPad 和 iPhone 之间设置了蓝牙连接。我在ViewController 中创建了我的测试代码,一切正常。现在,我将其移至 2 个管理器类,一个用于 CBCentralManager,一个用于 CBPeripheralManager,上面我创建了一个 BluetoothManager 的类,这是一个单例类,包含有关当前连接设备的一些信息。

但是,当我这样做时,我遇到了一个问题,似乎centralManager.connect() 调用实际上不起作用。我调试了我的整个代码,在那一行之后似乎什么都没有发生,我似乎无法弄清楚为什么会这样或者我实际上哪里出错了。

CentralManager 类

import Foundation
import CoreBluetooth

class CentralManager: NSObject 
    private var centralManager: CBCentralManager!
    var peripherals: [CBPeripheral] = []

    override init() 
        super.init()

        centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.main)
    


// MARK: - CBCentralManager Delegate Methods
extension CentralManager: CBCentralManagerDelegate 

    func centralManagerDidUpdateState(_ central: CBCentralManager) 
        switch central.state 
        case .poweredOn:
            centralManager.scanForPeripherals(withServices: [BLEConstants.serviceUUID], options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])
        default:
            break
        
    

    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) 
        if !peripherals.contains(peripheral) 
            peripheral.delegate = self
            peripherals.append(peripheral)
            centralManager.connect(peripheral, options: nil)
        
    

    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) 
        peripheral.discoverServices([BLEConstants.serviceUUID])
    

    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) 
        guard let peripheralIndex = peripherals.index(of: peripheral), BluetoothManager.shared.deviceCharacteristic[peripheral] != nil else  return 

        peripherals.remove(at: peripheralIndex)
        BluetoothManager.shared.deviceCharacteristic.removeValue(forKey: peripheral)
    



// MARK: - CBPeripheral Delegate Methods
extension CentralManager: CBPeripheralDelegate 

    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) 
        for service in peripheral.services! 
            if service.uuid == BLEConstants.serviceUUID 
                peripheral.discoverCharacteristics(nil, for: service)
            
        
    

    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) 
        for characteristic in service.characteristics! 
            let characteristic = characteristic as CBCharacteristic

            if BluetoothManager.shared.deviceCharacteristic[peripheral] == nil 
                BluetoothManager.shared.deviceCharacteristic[peripheral] = characteristic
            
        
    

    func peripheral(_ peripheral: CBPeripheral, didModifyServices invalidatedServices: [CBService]) 

    


PeripheralManager 类

class PeripheralManager: NSObject 
    private var peripheralManager: CBPeripheralManager!

    override init() 
        super.init()

        peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
    



// MARK: - Manage Methods
extension PeripheralManager 

    func updateAdvertising() 
        guard !peripheralManager.isAdvertising else  peripheralManager.stopAdvertising(); return 

        let advertisingData: [String: Any] = [CBAdvertisementDataServiceUUIDsKey: BLEConstants.serviceUUID,
                               CBAdvertisementDataLocalNameKey: BLEConstants.bleAdvertisementKey]
        peripheralManager.startAdvertising(advertisingData)
    

    func initializeService() 
        let service = CBMutableService(type: BLEConstants.serviceUUID, primary: true)

        let characteristic = CBMutableCharacteristic(type: BLEConstants.charUUID, properties: BLEConstants.charProperties, value: nil, permissions: BLEConstants.charPermissions)
        service.characteristics = [characteristic]

        peripheralManager.add(service)
    



// MARK: - CBPeripheralManager Delegate Methods
extension PeripheralManager: CBPeripheralManagerDelegate 

    func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) 
        if peripheral.state == .poweredOn 
            initializeService()
            updateAdvertising()
        
    

    func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) 
        for request in requests 
            if let value = request.value 
                let messageText = String(data: value, encoding: String.Encoding.utf8)
                print(messageText ?? "")
            
            self.peripheralManager.respond(to: request, withResult: .success)
        
    


BluetoothManager 类

class BluetoothManager 
    static let shared = BluetoothManager()
    private var centralManager: CentralManager!
    private var peripheralManager: PeripheralManager!

    var deviceCharacteristic: [CBPeripheral: CBCharacteristic] = [:]
    var connectedPeripherals: [CBPeripheral]  return centralManager.peripherals 

    func setup() 
        centralManager = CentralManager()
        peripheralManager = PeripheralManager()
    


然后在我的ViewController didLoad 我打电话给BluetoothManager.shared.setup()

有谁知道为什么这些设备似乎没有相互连接,或者之后的委托函数没有被调用?

【问题讨论】:

这可能只是一个大胆的猜测,但对象是在 Swift 中使用 'init' 方法实例化的,而不是使用 'setup'。您确定您的 BluetoothManager 已正确初始化吗? @fishinear true 但是我在调​​用ViewController 时调用了ViewController 中的设置函数,所以我基本上是在为它创建自己的init 函数,因为单例实例化了自己并且我无法控制何时发生这种情况。我做了设置方式,因为在常规初始化中没有一个委托函数被调用 【参考方案1】:

当静态shared 变量使用BluetoothManager() 初始化时,该过程开始。我不确定这在 Swift 中什么时候发生,要么是在程序的一开始,要么是在你第一次使用 BluetoothManager.setup 时。 变量的初始化调用BluetoothManagerinit() 方法。这将实例化一个CentralManager,并调用它的init() 方法。这将实例化一个CBCentralManager,它将启动蓝牙进程。

然后调用setup(),它将实例化一个新的CentralManager,并带有自己的CBCentralManager。我可以想象两个CBCentralManager 有问题。

要解决它,不要使用setup(),而是初始化init()中的变量。

要调试这种情况,请在所有init() 方法中设置断点。创建析构函数,并在其中放置断点。从技术上讲,无论如何您都需要析构函数,因为您需要从 CBCentralManager 对象中删除自己作为委托。


另请注意,您只能从centralManagerDidUpdateState 调用scanForPeripheralsCBCentralManager 在启动时可能已经处于 poweredOn 状态,这可能发生在另一个应用程序同时使用蓝牙时 - 或者当您的第一个 CBCentralManager 对象已经启动它时。在这种情况下,centralManagerDidUpdateState 将永远不会被调用。

【讨论】:

我刚刚创建了设置以控制何时设置管理器。但是我在设置startAdvertising 时确实发现了问题,我将ServiceUUID 作为对象而不是UUID's 的数组传递,这就是它没有按预期工作的原因【参考方案2】:

您确定您的单例已正确初始化吗?

试试这个:

import Foundation

private let singleton = Singleton()

class Singleton 

  static let sharedInstance : Singleton = 
    return singleton
  ()

  let cnetralManager = = CBCentralManager(delegate: self, queue: DispatchQueue.main)

【讨论】:

以上是关于CoreBluetooth 功能在 Singleton 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

使用 AltBeacon 库以 CoreBluetooth 格式做广告

CoreBluetooth - TouchID应用

iPhone 4S 上的 Corebluetooth 故障?

iOS CoreBluetooth 不太可能的错误

如何仅使用 CoreBluetooth 扫描信标

ruby 来自http://stackoverflow.com/questions/10039039/why-self-method-of-module-cannot-become-a-singlet