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

iOS开发:如何在onViewDisappear时取消URLSession网络请求?

解决ViewController消失时取消网络请求的问题

这个问题太常见了——快速切页后后台请求还在跑,等请求完成想更新UI时,ViewController已经被释放,自然就会弹出那个烦人的警告。下面给你两种针对性的解决方案,覆盖单个请求和多个请求的场景:

场景1:单个网络请求

核心思路是把请求任务保存为ViewController的属性,这样页面消失时能直接拿到它并取消。

步骤1:在ViewController中声明任务属性

在你的ViewController类里添加一个可选的URLSessionDataTask属性,用来存发起的请求:

class YourViewController: UIViewController {
    // 保存网络任务,方便后续取消操作
    private var dataTask: URLSessionDataTask?
    
    // 其他原有代码...
}

步骤2:发起请求时绑定任务到属性

修改你原来的请求代码,把创建好的task赋值给上面的属性,同时加[weak self]避免循环引用:

if let requestURL = URL(string: "https://www.example.com/file.php") {
    var urlRequest = URLRequest(url: requestURL)
    urlRequest.httpMethod = "POST"
    let postString = "email=\(loginUsername.text!)"
    urlRequest.httpBody = postString.data(using: .utf8)
    let session = URLSession.shared
    
    // 创建任务并保存到属性
    dataTask = session.dataTask(with: urlRequest) { [weak self] data, response, error in
        // 先判断任务是否被取消,若是直接返回,避免无效操作
        guard let self = self else { return }
        if let error = error as? URLError, error.code == .cancelled {
            return
        }
        
        if let data = data {
            do {
                if let jsonResult = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any] {
                    DispatchQueue.main.async{
                        // 只有在ViewController存在且任务未取消时,才执行UI操作
                        // do things
                    }
                }
            } catch {
                print("Error: \(error)")
            }
        }
    }
    dataTask?.resume()
}

步骤3:页面消失时取消任务

重写viewDidDisappear方法,在这里取消保存的任务:

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    // 取消网络任务
    dataTask?.cancel()
    dataTask = nil // 可选:清空属性,释放多余资源
}

场景2:多个网络请求

如果你的ViewController可能同时发起多个请求,用数组来管理所有活跃任务会更方便:

步骤1:声明任务数组属性

class YourViewController: UIViewController {
    // 保存所有活跃的网络任务
    private var activeTasks = [URLSessionDataTask]()
    
    // 其他原有代码...
}

步骤2:发起请求时添加到数组

if let requestURL = URL(string: "https://www.example.com/file.php") {
    // ... 构建请求的代码和之前一致 ...
    
    let task = session.dataTask(with: urlRequest) { [weak self] data, response, error in
        // 判断任务是否被取消
        if let error = error as? URLError, error.code == .cancelled {
            // 可选:从数组中移除已取消的任务,保持数组整洁
            self?.activeTasks.removeAll { $0 == task }
            return
        }
        
        // 后续数据处理、UI更新逻辑和之前一致...
    }
    activeTasks.append(task)
    task.resume()
}

步骤3:页面消失时批量取消所有任务

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    // 遍历所有任务并取消
    activeTasks.forEach { $0.cancel() }
    activeTasks.removeAll() // 清空数组,释放资源
}

额外提示

  • cancel()调用后,任务的completion handler还是会触发,但此时error会是URLError.cancelled,所以一定要在handler里判断这个情况,避免执行无效的UI操作。
  • [weak self]是必须的,它能避免ViewController被网络任务强引用而无法释放,这也是触发“找不到ViewController”警告的核心原因之一。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 03:46:35