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

