使用Alamofire请求数据时,未完成请求销毁ViewController导致其无法释放
Hey Anna, the issue you're facing is a classic strong reference cycle (memory leak) caused by how your Alamofire request's closure captures self. Let's break this down and fix it step by step:
The Root Cause
When you use Alamofire.request().responseJSON, the closure you pass in automatically captures a strong reference to self (since you're accessing self.arrData, self.tbvData, etc.). Even after you dismiss the ViewController, the ongoing network request keeps that strong reference alive—so ARC can't deallocate the ViewController from memory.
On top of that, I noticed a small logic error in your code: you're trying to process data when an error exists, which is backwards! We'll fix that too.
Fix 1: Break the Reference Cycle with Weak Self
The simplest fix is to use a weak reference to self in the closure. This tells Swift not to keep a strong hold on the ViewController, allowing it to be released even if the request is still running. We'll also add a guard check to make sure the ViewController still exists before updating the UI (no point in reloading a table view that's already gone!).
Here's the revised code:
func fetchingDataFromServer(url: String, completed: @escaping (Error?) -> Void) { Alamofire.request(url) .responseJSON { [weak self] response in // Capture weak self here // Make sure self still exists before proceeding guard let strongSelf = self else { completed(nil) // ViewController is gone—finish the callback return } if let result = response.result.value as? [[String: String]] { if let error = response.error { // Error occurred: pass it to the completion handler completed(error) } else { // No error: process data and update UI strongSelf.arrData = strongSelf.pasteData(result) strongSelf.tbvData.reloadData() completed(nil) } } else { // Data format doesn't match what we expected let formatError = NSError(domain: "DataMismatch", code: -1, userInfo: [ NSLocalizedDescriptionKey: "Server returned unexpected data format" ]) completed(formatError) } } }
Fix 2: Cancel the Request When the ViewController is Dismissed
For extra safety (and to save network resources), you can cancel the ongoing request when the ViewController is about to be deallocated. This ensures the closure doesn't run at all once the ViewController is gone.
First, store a reference to the request in your ViewController:
class YourViewController: UIViewController { // Hold a reference to the ongoing request private var activeDataRequest: DataRequest? func fetchingDataFromServer(url: String, completed: @escaping (Error?) -> Void) { // Save the request reference activeDataRequest = Alamofire.request(url) .responseJSON { [weak self] response in guard let strongSelf = self else { completed(nil) return } // Same processing logic as above... if let result = response.result.value as? [[String: String]] { if let error = response.error { completed(error) } else { strongSelf.arrData = strongSelf.pasteData(result) strongSelf.tbvData.reloadData() completed(nil) } } else { let formatError = NSError(domain: "DataMismatch", code: -1, userInfo: [ NSLocalizedDescriptionKey: "Server returned unexpected data format" ]) completed(formatError) } // Clear the request reference once done strongSelf.activeDataRequest = nil } } // Cancel the request when the ViewController is deallocated deinit { activeDataRequest?.cancel() print("ViewController successfully deallocated!") // Use this to verify the fix } }
How to Verify the Fix
You can use Xcode's Memory Graph Debugger (the icon that looks like three circles connected by lines) to check if the ViewController is being released. After dismissing it, if it doesn't show up in the memory graph, you've fixed the leak!
内容的提问来源于stack exchange,提问作者Anna

