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

如何在Alamofire 4.7.2中等待所有网络任务取消后执行登出?

嘿,这个问题我之前维护老Swift 3项目的时候也碰到过!直接同步取消任务就执行登出确实会有 race condition,毕竟Alamofire的getAllTasks是异步获取任务列表的,你之前的代码肯定是把doLogout()放在了回调外面,导致登出逻辑抢在任务取消前执行了对吧?下面给你几个适配Alamofire 4.7.2 + iOS 9+的靠谱方案,完全不用定时器凑数:

解决方案:确保所有网络任务取消完成后再执行登出

方案1:最简单的调整——把登出逻辑挪到任务回调里

Alamofire的getAllTasks(completion:)本身是异步执行的,你只需要把登出代码放到这个回调内部,就能确保只有当所有活跃任务都被取消后,才触发登出流程:

// 获取所有后台任务并取消
SessionManager.default.getAllTasks { [weak self] tasks in
    // 先过滤掉已经完成的任务,只处理活跃的
    let activeTasks = tasks.filter { $0.state != .completed }
    activeTasks.forEach { $0.cancel() }
    
    // 现在所有活跃任务都标记为取消了,执行登出
    self?.doLogout()
}

这个方案足够应对大部分场景,毕竟cancel()会立即终止任务的网络请求,后续的清理工作不会影响登出流程。如果你的场景要求绝对确保所有任务的回调都执行完毕再登出,可以试试下面的方案2。

方案2:用DispatchGroup跟踪任务的完成状态

如果需要等每个任务彻底进入completed状态(包括取消后的收尾工作)再登出,用DispatchGroup来跟踪是最稳妥的:

let taskGroup = DispatchGroup()
SessionManager.default.getAllTasks { [weak self] tasks in
    let activeTasks = tasks.filter { $0.state != .completed }
    
    activeTasks.forEach { task in
        taskGroup.enter()
        
        // 监听任务状态变化,一旦完成就离开group
        let stateObservation = task.observe(\.state) { [weak task] _, _ in
            guard let task = task, task.state == .completed else { return }
            taskGroup.leave()
            // 记得销毁观察者,避免内存泄漏
            stateObservation.invalidate()
        }
        
        task.cancel()
    }
    
    // 等所有任务都完成后,回到主队列执行登出
    taskGroup.notify(queue: .main) {
        self?.doLogout()
    }
}

这个方案会耐心等待每个活跃任务都彻底结束,完全避免了登出和任务取消的时序问题。

方案3:封装自定义SessionManager,长期维护更省心

如果你的项目经常需要批量取消任务再执行操作,可以封装一个自定义的SessionManager子类,把任务管理逻辑集中起来:

class CustomSessionManager: SessionManager {
    private var activeTasks = [URLSessionTask]()
    
    override func task(_ task: URLSessionTask, didCompleteWithError error: Error?) {
        super.task(task, didCompleteWithError: error)
        // 任务完成后从列表移除
        if let index = activeTasks.index(of: task) {
            activeTasks.remove(at: index)
        }
    }
    
    override func add(_ task: URLSessionTask) {
        super.add(task)
        // 新任务加入列表
        activeTasks.append(task)
    }
    
    func cancelAllActiveTasks(completion: @escaping () -> Void) {
        guard !activeTasks.isEmpty else {
            completion()
            return
        }
        
        let group = DispatchGroup()
        activeTasks.forEach { task in
            group.enter()
            task.observe(\.state) { [weak task] _, _ in
                guard let task = task, task.state == .completed else { return }
                group.leave()
            }
            task.cancel()
        }
        
        group.notify(queue: .main, execute: completion)
    }
}

// 使用的时候就很简单了
let customSession = CustomSessionManager.default
customSession.cancelAllActiveTasks { [weak self] in
    self?.doLogout()
}

把任务管理封装起来后,后续再做类似操作就不用重复写代码了,调用方只需要关注业务逻辑就行。

几个小提醒

  • 闭包里一定要用[weak self],Swift 3里内存泄漏的坑可不少!
  • 记得过滤掉已完成的任务,不然会做无效操作,还可能导致DispatchGroup的enter/leave不匹配,引发死锁。
  • 如果你的登出流程包含自己的网络请求(比如调用服务器的登出API),一定要给这个请求做个标记,取消任务的时候排除它,别把登出请求也给取消了😂

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 06:42:15