使用URLSession dataTask的completionHandler时如何正确管理任务?
URLSession多请求并行管理的问题解决方法
原代码核心问题
- 捕获顺序错误:闭包中直接引用
task时,该变量尚未完成初始化,触发编译错误Closure captures 'task' before it is declared。 - Sendable合规性警告:
@Sendable闭包捕获了被修改的task引用(插入Set操作),编译器判定存在线程安全风险。 - 线程竞争隐患:未对
tasks集合的多线程访问做同步处理,后台队列的completionHandler与主线程的取消操作可能同时修改集合,引发崩溃。
最优实现方案
通过串行队列保证线程安全+弱引用避免捕获问题,同时满足Sendable协议要求:
class API { private var tasks: Set<URLSessionTask> = [] // 串行队列,所有对tasks的操作必须通过该队列执行,避免线程竞争 private let taskSyncQueue = DispatchQueue(label: "com.your.api.task.sync") func sendRequest(path: String, body: [String: Any], completionHandler: @escaping @Sendable (APIResponse) -> Void) { // 构造请求(根据实际需求补充) guard let url = URL(string: "https://your-api-base/\(path)") else { completionHandler(.failure(.invalidURL)) return } var request = URLRequest(url: url) request.httpMethod = "POST" request.httpBody = try? JSONSerialization.data(withJSONObject: body) request.setValue("application/json", forHTTPHeaderField: "Content-Type") let task = URLSession.shared.dataTask(with: request) { [weak self] data, response, error in // 处理响应,转换为自定义APIResponse(根据实际需求补充) let apiResponse: APIResponse if let error = error { apiResponse = .failure(.networkError(error)) } else if let data = data { // 解析data为响应模型 apiResponse = .success(/* 解析结果 */) } else { apiResponse = .failure(.emptyResponse) } // 异步到同步队列移除任务,保证线程安全 self?.taskSyncQueue.async { self?.tasks.remove(task) } // 回调结果到调用线程(默认是后台队列,可根据需求切换到主线程) completionHandler(apiResponse) } // 同步队列中插入任务,避免多线程竞争 taskSyncQueue.async { self.tasks.insert(task) } task.resume() } func cancelAllTasks() { // 同步执行取消操作,保证所有任务被处理完成后再返回 taskSyncQueue.sync { self.tasks.forEach { $0.cancel() } self.tasks.removeAll() } } } // 自定义APIResponse示例(根据实际需求调整) enum APIResponse { case success(Any) case failure(Error) } enum APIError: Error { case invalidURL case networkError(Error) case emptyResponse }
方案说明
- 线程安全保障:所有对
tasks集合的操作(插入、移除、遍历取消)都通过串行队列执行,彻底避免多线程竞争问题。 - 解决捕获问题:闭包使用
weak self间接访问任务集合,不再直接捕获task变量,消除编译错误与Sendable警告。 - Sendable合规:闭包仅捕获符合Sendable要求的弱引用与不可变的
task实例,满足Swift并发安全规范。 - 高效取消逻辑:取消操作一次性遍历并清空所有任务,避免遍历过程中修改集合引发的异常。
内容的提问来源于stack exchange,提问作者Wes Souza
相关产品推荐
相关产品推荐

