Starscream闭包回调 vs 委托:选择适合的事件处理方式
你是否在使用Starscream开发WebSocket应用时,纠结于该用闭包回调还是委托模式处理事件?本文将通过实际代码对比,帮你快速掌握两种方式的优缺点及适用场景,5分钟内做出最优选择。
两种事件处理模式的核心实现
Starscream同时支持闭包回调和委托模式处理WebSocket事件,这两种模式在Sources/Starscream/WebSocket.swift中均有定义:
// 委托模式协议定义
public protocol WebSocketDelegate: AnyObject {
func didReceive(event: WebSocketEvent, client: WebSocketClient)
}
// 闭包回调属性定义
open class WebSocket: WebSocketClient, EngineDelegate {
public weak var delegate: WebSocketDelegate?
public var onEvent: ((WebSocketEvent) -> Void)?
// ...
}
两种模式共享相同的事件类型集合WebSocketEvent,包含连接状态、数据接收、错误等所有可能的WebSocket事件:
public enum WebSocketEvent {
case connected([String: String]) // 连接成功
case disconnected(String, UInt16) // 断开连接
case text(String) // 接收文本消息
case binary(Data) // 接收二进制消息
case pong(Data?) // 接收到Pong响应
case ping(Data?) // 接收到Ping请求
case error(Error?) // 发生错误
case viabilityChanged(Bool) // 连接可用性变化
case reconnectSuggested(Bool) // 建议重连
case cancelled // 连接取消
case peerClosed // 对方关闭连接
}
委托模式:严谨的面向对象方式
委托模式通过实现WebSocketDelegate协议来处理事件,适合需要严格遵循面向对象设计原则的场景。
实现步骤
- 遵循协议:让你的类实现
WebSocketDelegate协议 - 设置代理:将WebSocket实例的
delegate属性设为当前类 - 实现回调方法:重写
didReceive(event:client:)方法处理所有事件
代码示例
class ChatViewController: UIViewController, WebSocketDelegate {
private var socket: WebSocket!
override func viewDidLoad() {
super.viewDidLoad()
// 创建WebSocket请求
let request = URLRequest(url: URL(string: "wss://chat.example.com")!)
socket = WebSocket(request: request)
// 设置代理
socket.delegate = self
// 连接服务器
socket.connect()
}
// 实现委托方法处理所有事件
func didReceive(event: WebSocketEvent, client: WebSocketClient) {
switch event {
case .connected(let headers):
print("连接成功,响应头: \(headers)")
showConnectionStatus(message: "已连接到聊天服务器")
case .text(let message):
print("收到消息: \(message)")
displayNewMessage(text: message)
case .disconnected(let reason, let code):
print("断开连接: \(reason) (代码: \(code))")
showDisconnectionAlert(reason: reason)
case .error(let error):
print("发生错误: \(error?.localizedDescription ?? "未知错误")")
showError(message: error?.localizedDescription ?? "连接出错")
default:
// 处理其他事件...
break
}
}
// UI更新方法
private func showConnectionStatus(message: String) {
// 更新UI显示连接状态
}
private func displayNewMessage(text: String) {
// 在聊天界面显示新消息
}
private func showDisconnectionAlert(reason: String) {
// 显示断开连接提示
}
private func showError(message: String) {
// 显示错误提示
}
}
适用场景
- 大型项目:需要清晰的代码组织结构和职责分离
- 多事件处理:当需要集中处理多种事件类型时
- 代码复用:多个WebSocket实例共享相同的事件处理逻辑
- 面向对象设计:遵循iOS开发的传统设计模式
优缺点分析
| 优点 | 缺点 |
|---|---|
| 符合iOS传统设计模式,易于理解 | 需要额外遵循协议和设置代理 |
| 集中处理所有事件,逻辑清晰 | 代码相对冗长 |
| 便于进行单元测试和模拟 | 事件处理逻辑与类紧密耦合 |
| 支持多个实例共享同一个代理 | - |
闭包回调:简洁的函数式风格
闭包回调模式通过设置onEvent属性来处理事件,适合需要简洁代码和快速开发的场景。
实现步骤
- 创建WebSocket实例:初始化WebSocket对象
- 设置闭包:为
onEvent属性赋值一个闭包 - 处理事件:在闭包中使用switch语句处理不同事件
代码示例
class LiveScoreViewController: UIViewController {
private var socket: WebSocket!
override func viewDidLoad() {
super.viewDidLoad()
// 创建WebSocket请求
let request = URLRequest(url: URL(string: "wss://scores.example.com/live")!)
socket = WebSocket(request: request)
// 设置事件闭包回调
socket.onEvent = { [weak self] event in
guard let self = self else { return }
switch event {
case .connected(let headers):
print("连接成功,响应头: \(headers)")
self.updateConnectionStatus(isConnected: true)
case .text(let scoreData):
print("收到比分数据: \(scoreData)")
self.updateScoreboard(data: scoreData)
case .disconnected(let reason, let code):
print("断开连接: \(reason) (代码: \(code))")
self.updateConnectionStatus(isConnected: false)
self.showReconnectionPrompt()
case .error(let error):
print("发生错误: \(error?.localizedDescription ?? "未知错误")")
self.showErrorBanner(message: "连接错误")
default:
// 处理其他事件...
break
}
}
// 连接服务器
socket.connect()
}
// UI更新方法
private func updateConnectionStatus(isConnected: Bool) {
// 更新连接状态指示器
}
private func updateScoreboard(data: String) {
// 解析数据并更新比分板
}
private func showReconnectionPrompt() {
// 显示重新连接提示
}
private func showErrorBanner(message: String) {
// 显示错误提示条
}
}
适用场景
- 小型项目:快速开发和简单的事件处理需求
- 单一功能模块:WebSocket事件处理逻辑相对简单
- 函数式编程风格:偏好简洁代码和闭包语法
- 临时或一次性连接:短期使用的WebSocket连接
优缺点分析
| 优点 | 缺点 |
|---|---|
| 代码简洁,实现快速 | 复杂事件处理时可能导致闭包过大 |
| 上下文清晰,无需额外协议 | 多个实例需要重复编写相似逻辑 |
| 无需管理代理生命周期 | 可能导致"回调地狱"(复杂场景) |
| 便于捕获上下文变量 | 调试相对困难 |
性能与内存考量
内存管理对比
两种模式在内存管理上有不同表现:
- 委托模式:使用
weak引用避免循环引用,需要手动管理生命周期 - 闭包模式:需使用
[weak self]避免循环引用,尤其在长时间运行的连接中
性能差异
在性能方面,两种模式差异不大,但有几点需要注意:
- 委托模式通过协议方法调用,有轻微的动态派发开销
- 闭包模式是直接函数调用,理论上略快,但实际使用中几乎无感知
- 大量事件频繁触发时,两种模式性能表现相近
如何选择?决策流程图
混合使用策略
在某些场景下,你也可以混合使用两种模式:
// 混合使用委托和闭包
class HybridViewController: UIViewController, WebSocketDelegate {
private var socket: WebSocket!
override func viewDidLoad() {
super.viewDidLoad()
let request = URLRequest(url: URL(string: "wss://hybrid.example.com")!)
socket = WebSocket(request: request)
// 设置委托处理大多数事件
socket.delegate = self
// 使用闭包处理特定事件
socket.onEvent = { event in
if case .reconnectSuggested(let shouldReconnect) = event {
// 闭包专门处理重连建议事件
print("是否建议重连: \(shouldReconnect)")
if shouldReconnect {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in
self?.socket.connect()
}
}
}
}
socket.connect()
}
// 委托方法处理其他事件
func didReceive(event: WebSocketEvent, client: WebSocketClient) {
switch event {
case .connected(let headers):
// 处理连接事件
break
case .text(let message):
// 处理文本消息
break
// 处理其他事件...
default:
break
}
}
}
最佳实践与常见问题
线程安全
Starscream默认在主线程回调事件,可通过callbackQueue属性自定义:
// 修改回调队列
socket.callbackQueue = DispatchQueue(label: "com.example.websocket", attributes: .concurrent)
避免常见陷阱
-
循环引用:
- 委托模式:已通过
weak关键字避免 - 闭包模式:务必使用
[weak self]
- 委托模式:已通过
-
事件处理完整性: 确保处理所有可能的事件类型,避免遗漏重要状态变化
-
UI更新: 所有UI更新必须在主线程执行,Starscream默认已处理,但自定义队列时需注意
调试技巧
- 使用详细日志记录事件:
func didReceive(event: WebSocketEvent, client: WebSocketClient) {
switch event {
case .connected(let headers):
print("🔗 连接成功,响应头: \(headers)")
case .disconnected(let reason, let code):
print("🔌 断开连接: \(reason) (代码: \(code))")
case .text(let message):
print("📩 收到文本: \(message)")
case .binary(let data):
print("📦 收到二进制数据: \(data.count)字节")
case .error(let error):
print("❌ 错误: \(error?.localizedDescription ?? "未知错误")")
// 其他事件...
default:
print("📡 其他事件: \(event)")
}
}
- 使用断点调试事件流程,特别是错误处理部分
总结
Starscream提供的两种事件处理模式各有优势,选择时应考虑项目规模、团队习惯和具体使用场景:
- 委托模式适合大型项目和需要严格代码组织的场景,提供更好的可维护性和扩展性
- 闭包模式适合小型项目和快速开发,代码更简洁直观
无论选择哪种模式,都应遵循内存管理最佳实践,避免循环引用,并确保正确处理所有事件类型。通过本文的指南,你可以根据具体需求做出最合适的选择,构建稳定高效的WebSocket应用。
官方示例代码:examples/SimpleTest/ 提供了更多实际应用场景,你可以参考其中的实现方式来加深理解。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



