正如其他人指出的那样,应该避免使用全局变量。如果你把它作为某个类的属性,就可以避免这个问题。
FWIW,严格来说,问题不在于全局变量的使用,而在于您选择的特定变量名。例如,更改该全局变量的名称,它可以正常编译:
import UIKit
import CoreBluetooth
var manager: CBCentralManager!
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
manager = CBCentralManager(delegate: self, queue: nil)
}
}
extension ViewController: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
print("BLE powered on")
} else {
print("Something wrong with BLE")
}
}
}
问题在于,在存在多个同名协议方法(例如,centralManager(_:didConnect:)、centralManager(_:didDisconnectPeripheral:error:) 等)的情况下,编译器在解析这个全局符号 centralManager 时遇到问题。人们会期望编译器能够在给定上下文的情况下消除全局方法和协议方法之间的歧义,但事实并非如此。 (也可以通过在符合UITableViewDataSource 的视图控制器中定义一个名为tableView 的全局变量来体现这个问题。)
归根结底,技术问题是全局变量名的选择。但正确的答案是它根本不应该是全局的。
一旦你解决了眼前的问题,你可能会重新考虑是否将这个蓝牙代码放在视图控制器中。当然,在简单的演示应用程序中,我们经常会采用这样的捷径,但是随着应用程序的增长,视图控制器会变得笨拙。此外,您最终会希望将蓝牙功能添加到其他视图控制器,并且您将复制代码。
相反,创建自己的管理器类,将CBCentralManager 和CBPeripheral 的细节从视图控制器中抽象出来。然后,您可以为您的视图控制器提供一个很好的简单界面,用于与管理器进行交互。
例如,你可能有类似的东西:
class BluetoothManager: NSObject {
var isAvailableHandler: ((Bool) -> Void)?
private var manager: CBCentralManager? = nil
override init() {
super.init()
}
func start() {
guard manager == nil else { return }
manager = CBCentralManager(delegate: self, queue: nil)
}
}
extension BluetoothManager: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
isAvailableHandler?(central.state == .poweredOn)
}
}
然后您可以执行以下操作:
class ViewController: UIViewController {
private let deviceManager = BluetoothManager()
override func viewDidLoad() {
super.viewDidLoad()
deviceManager.isAvailableHandler = { [weak self] isAvailable in
print(isAvailable ? "Powered on" : "Not available")
}
deviceManager.start()
}
}
现在,在这个例子中,我只处理centralManagerDidUpdateState,但是你需要添加额外的闭包或者你自己的协议来处理你想要暴露给你的视图控制器的任何事件。但希望这能说明这个想法。