You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用URLSession dataTask的completionHandler时如何正确管理任务?

URLSession多请求并行管理的问题解决方法

原代码核心问题

  1. 捕获顺序错误:闭包中直接引用task时,该变量尚未完成初始化,触发编译错误Closure captures 'task' before it is declared。
  2. Sendable合规性警告:@Sendable闭包捕获了被修改的task引用(插入Set操作),编译器判定存在线程安全风险。
  3. 线程竞争隐患:未对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
}

方案说明

  1. 线程安全保障:所有对tasks集合的操作(插入、移除、遍历取消)都通过串行队列执行,彻底避免多线程竞争问题。
  2. 解决捕获问题:闭包使用weak self间接访问任务集合,不再直接捕获task变量,消除编译错误与Sendable警告。
  3. Sendable合规:闭包仅捕获符合Sendable要求的弱引用与不可变的task实例,满足Swift并发安全规范。
  4. 高效取消逻辑:取消操作一次性遍历并清空所有任务,避免遍历过程中修改集合引发的异常。

内容的提问来源于stack exchange,提问作者Wes Souza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 05:20:30