如何避免多个自定义类重复实现BLE协议的必需方法?
解决方案:复用BLE协议实现的最佳实践
嘿,这个问题问到点子上了——复用协议实现绝对是iOS开发里减少冗余代码、降低维护成本的关键,尤其是你说未来还要加更多类似类的情况。咱们先把问题拆开来聊:
一、复制粘贴可行吗?
短期来看,复制粘贴确实能让你的新MotorViewController快速跑起来,但长期绝对是个大坑:
- 冗余代码:每个类里都有几乎一样的协议方法实现,项目体积无端膨胀;
- 维护噩梦:如果后续需要修改BLE逻辑(比如新增协议方法、调整扫描策略),你得手动修改所有复制过代码的类,很容易漏改导致bug;
- 违反DRY(Don't Repeat Yourself)原则,这是开发里的大忌。
所以除非是临时测试用,否则完全不推荐复制粘贴。
二、复用协议方法的最佳方案
针对你的场景(多个类逻辑流程一致,仅数据处理/属性不同),有两种非常合适的方案,推荐优先用第一种:
方案1:协议扩展 + 自定义协议(最灵活)
利用Swift的协议扩展特性,把CBCentralManagerDelegate和CBPeripheralDelegate的通用逻辑抽离到扩展里,再通过一个自定义协议让各个类实现自己的专属逻辑。
步骤如下:
- 定义一个自定义协议,规定每个类需要实现的专属方法:
// 自定义协议,让需要处理BLE数据的类遵守 protocol BLEDataHandler: CBCentralManagerDelegate, CBPeripheralDelegate { /// 处理收到的BLE数据,由子类实现专属解析逻辑 func handleBLEData(_ data: Data) // 可以根据需求添加其他必须实现的属性/方法,比如蓝牙服务UUID、特征UUID等 }
- 给两个BLE协议写扩展,实现通用逻辑:
// 扩展CBCentralManagerDelegate,实现通用的蓝牙状态管理、扫描逻辑 extension CBCentralManagerDelegate where Self: BLEDataHandler { func centralManagerDidUpdateState(_ central: CBCentralManager) { switch central.state { case .poweredOn: // 通用扫描逻辑,比如扫描指定服务UUID的外设 central.scanForPeripherals(withServices: [YourServiceUUID], options: nil) case .poweredOff: // 通用提示逻辑,比如弹窗提示用户打开蓝牙 print("蓝牙已关闭,请打开后重试") default: print("蓝牙状态异常:\(central.state.rawValue)") } } // 其他通用的CBCentralManagerDelegate方法,比如发现外设的逻辑 func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { // 通用连接逻辑,比如判断外设名称后连接 if peripheral.name == "YourPSoC4Device" { central.stopScan() connectedPeripheral = peripheral peripheral.delegate = self central.connect(peripheral, options: nil) } } } // 扩展CBPeripheralDelegate,实现通用的服务/特征发现、数据接收逻辑 extension CBPeripheralDelegate where Self: BLEDataHandler { func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { guard let services = peripheral.services else { return } // 通用服务发现逻辑,遍历服务找目标特征 for service in services { if service.uuid == YourServiceUUID { peripheral.discoverCharacteristics([YourCharacteristicUUID], for: service) } } } func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { guard let characteristics = service.characteristics else { return } // 通用特征订阅逻辑,订阅目标特征的通知 for characteristic in characteristics { if characteristic.uuid == YourCharacteristicUUID { peripheral.setNotifyValue(true, for: characteristic) } } } func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { guard let data = characteristic.value else { return } // 调用自定义协议的方法,让子类处理专属数据 self.handleBLEData(data) } }
- 让你的ViewController遵守自定义协议,实现专属数据处理:
比如PressureViewController:
class PressureViewController: UIViewController, BLEDataHandler { private var voltage: UInt32 = 0 var centralManager: CBCentralManager! var connectedPeripheral: CBPeripheral? override func viewDidLoad() { super.viewDidLoad() centralManager = CBCentralManager(delegate: self, queue: nil) } // 实现自定义协议的方法,处理电压数据 func handleBLEData(_ data: Data) { // 将Data转成UInt32类型的电压值 voltage = data.withUnsafeBytes { $0.load(as: UInt32.self) } // 这里可以更新UI或做其他业务逻辑 updateVoltageUI() } private func updateVoltageUI() { // 更新UI的逻辑 } }
MotorViewController:
class MotorViewController: UIViewController, BLEDataHandler { private var radialPosition: UInt8 = 0 var centralManager: CBCentralManager! var connectedPeripheral: CBPeripheral? override func viewDidLoad() { super.viewDidLoad() centralManager = CBCentralManager(delegate: self, queue: nil) } // 实现自定义协议的方法,处理径向位置数据 func handleBLEData(_ data: Data) { // 将Data转成UInt8类型的径向位置值 radialPosition = data.withUnsafeBytes { $0.load(as: UInt8.self) } // 更新UI或其他业务逻辑 updatePositionUI() } private func updatePositionUI() { // 更新UI的逻辑 } }
这种方案的优势是灵活性极高:
- 不需要继承,避免了ViewController继承链复杂的问题;
- 未来如果有非ViewController的类(比如ViewModel)需要处理BLE逻辑,也可以遵守
BLEDataHandler协议直接复用; - 如果某个类需要特殊处理某个协议方法,直接在类里重写即可,会覆盖扩展里的默认实现。
方案2:基类继承(适合有共同UI/逻辑的场景)
如果你的多个ViewController除了BLE逻辑,还有其他共同的UI或业务逻辑,可以创建一个基类,让它实现BLE协议的通用方法,子类继承后重写专属逻辑:
class BLEBaseViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate { var centralManager: CBCentralManager! var connectedPeripheral: CBPeripheral? override func viewDidLoad() { super.viewDidLoad() centralManager = CBCentralManager(delegate: self, queue: nil) } // 通用的CBCentralManagerDelegate方法 func centralManagerDidUpdateState(_ central: CBCentralManager) { // 通用逻辑... } // 通用的CBPeripheralDelegate方法 func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { guard let data = characteristic.value else { return } // 调用子类必须实现的方法 handleBLEData(data) } // 抽象方法,强制子类实现 func handleBLEData(_ data: Data) { fatalError("子类必须实现handleBLEData方法") } // 其他通用方法... }
然后子类继承基类:
class PressureViewController: BLEBaseViewController { private var voltage: UInt32 = 0 override func handleBLEData(_ data: Data) { voltage = data.withUnsafeBytes { $0.load(as: UInt32.self) } updateVoltageUI() } private func updateVoltageUI() { // 更新UI逻辑 } }
这种方案的缺点是受限于Swift的单继承特性,如果你的ViewController已经继承了其他基类,就没法用了,所以灵活性不如协议扩展。
总结
- 复制粘贴短期可行,但长期维护成本极高,不推荐;
- 优先选择协议扩展+自定义协议的方案,灵活性最高,适合未来扩展;
- 如果多个ViewController有大量共同UI/逻辑,再考虑基类继承的方案。
内容的提问来源于stack exchange,提问作者Anthony_b
相关产品推荐
相关产品推荐

