如何在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
相关产品推荐
相关产品推荐

