如何处理sendMessage异步调用?WatchKit是否自动实现请求排队?
解决WCSession.sendMessage的异步排队问题
首先明确说:WatchKit不会自动实现消息排队机制,官方文档里确实没有相关说明,所以你得自己处理多个异步触发的sendMessage请求。
不用写复杂的自定义队列类,有两种简便的实现方式,都是基于系统原生的队列机制:
方法一:串行DispatchQueue+请求数组(轻量推荐)
这种方式用一个串行队列来管理请求的入队和出队,搭配一个数组存储待处理的请求,再用一个标记位跟踪当前是否正在发送消息。
- 先定义一个简单的结构体来存储请求参数(比类更简洁):
struct MessageRequest { let message: [String: Any] let replyHandler: (([String: Any]) -> Void)? let errorHandler: ((Error) -> Void)? }
- 初始化队列、请求数组和状态标记:
private let messageQueue = DispatchQueue(label: "com.your.app.wc.message.queue") private var pendingRequests: [MessageRequest] = [] private var isCurrentlySending = false
- 封装入队和处理逻辑:
// 对外暴露的方法,用来添加消息请求 func enqueueMessage(_ message: [String: Any], replyHandler: (([String: Any]) -> Void)? = nil, errorHandler: ((Error) -> Void)? = nil) { messageQueue.async { // 把请求加入待处理数组 self.pendingRequests.append(MessageRequest(message: message, replyHandler: replyHandler, errorHandler: errorHandler)) // 如果当前没有正在发送的消息,就处理下一个请求 if !self.isCurrentlySending { self.processNextRequest() } } } // 内部方法,处理下一个待发送的请求 private func processNextRequest() { guard !pendingRequests.isEmpty else { // 没有待处理请求,标记状态为空闲 isCurrentlySending = false return } isCurrentlySending = true // 取出最早的请求(FIFO) let nextRequest = pendingRequests.removeFirst() // 调用sendMessage WCSession.default.sendMessage(nextRequest.message, replyHandler: { reply in // 回到串行队列处理回调和下一个请求 self.messageQueue.async { // 执行回调(注意如果需要更新UI,要切回主队列) DispatchQueue.main.async { nextRequest.replyHandler?(reply) } // 处理完当前请求,继续下一个 self.processNextRequest() } }, errorHandler: { error in self.messageQueue.async { DispatchQueue.main.async { nextRequest.errorHandler?(error) } self.processNextRequest() } }) }
方法二:OperationQueue串行执行
利用OperationQueue的maxConcurrentOperationCount属性,把它设为1,就能自动实现串行执行。每个sendMessage请求包装成一个BlockOperation,注意要让Operation等待sendMessage的异步回调完成,不然队列会立刻执行下一个Operation。
private let messageOperationQueue = OperationQueue() // 在初始化方法里配置队列 init() { messageOperationQueue.maxConcurrentOperationCount = 1 // 只允许同时执行一个Operation } func enqueueMessage(_ message: [String: Any], replyHandler: (([String: Any]) -> Void)? = nil, errorHandler: ((Error) -> Void)? = nil) { let messageOperation = BlockOperation { [weak self] in guard let self = self else { return } // 用信号量等待sendMessage的异步回调完成 let semaphore = DispatchSemaphore(value: 0) WCSession.default.sendMessage(message, replyHandler: { reply in // 回调切回主队列(如果要更新UI的话) DispatchQueue.main.async { replyHandler?(reply) } semaphore.signal() // 通知信号量,请求完成 }, errorHandler: { error in DispatchQueue.main.async { errorHandler?(error) } semaphore.signal() }) semaphore.wait() // 阻塞当前Operation,直到回调触发 } messageOperationQueue.addOperation(messageOperation) }
两种方法对比
- 方法一更轻量,不需要用到信号量,逻辑更直观,适合大多数场景。
- 方法二利用Operation的特性,如果后续需要给请求添加依赖、取消等操作,会更灵活。
不管用哪种方法,都能实现你要的FIFO排队效果,避免多次sendMessage同时调用导致的问题。
内容的提问来源于stack exchange,提问作者Reinhard Männer
相关产品推荐
相关产品推荐

