带异步闭包的for循环致Segue传数组为空崩溃的解决问询
兄弟,我太懂这种异步操作没同步好就跳转的崩溃了!你猜的没错,核心问题就是循环里的网络请求是异步执行的——循环跑完的瞬间,大部分请求可能还在后台处理,这时候你直接触发Segue,数组自然是空的,新控制器拿到nil可不就崩溃了嘛。
下面给你两种靠谱的解决方案,按需选就行:
解决方案:同步异步操作后再执行Segue
方法一:传统GCD DispatchGroup(兼容所有Swift版本)
用DispatchGroup来跟踪所有异步请求的完成状态,等全部请求结束后再跳转:
// 先定义要传递的数组 var resultArray: [String] = [] // 创建一个调度组,用来跟踪异步任务 let taskGroup = DispatchGroup() // 遍历你的数据源 for dataItem in yourDataSourceArray { // 每次循环前,告诉组:我要开始一个任务了 taskGroup.enter() // 你的异步网络请求(这里假设你有个带回调的请求方法) yourNetworkRequestMethod(for: dataItem) { [weak self] responseString in // 用defer确保不管请求成功/失败,都告诉组任务完成了 defer { taskGroup.leave() } guard let self = self, let string = responseString else { return } // 把结果追加到数组 self.resultArray.append(string) } } // 所有任务都完成后,在主线程执行Segue taskGroup.notify(queue: .main) { [weak self] in guard let self = self else { return } // 这里触发Segue,把数组作为sender传过去 self.performSegue(withIdentifier: "YourSegueIdentifier", sender: self.resultArray) }
关键细节:
enter()和leave()必须成对出现,不然组会一直等待,永远不会触发notify- 用
defer包裹leave(),确保即使请求失败(比如网络错误),也不会让组卡住 - 一定要在主线程执行
performSegue,因为UI操作必须在主线程
方法二:Swift 5.5+ Async/Await(更简洁推荐)
如果你用的是Swift 5.5及以上版本,Async/Await会让代码更清爽,不用手动管理组:
首先把你的回调式网络请求改成Async/Await版本:
// 封装原有的回调请求为async函数 func fetchDataAsync(for item: YourDataType) async throws -> String { return try await withCheckedThrowingContinuation { continuation in yourNetworkRequestMethod(for: item) { result in if let string = result { continuation.resume(returning: string) } else { continuation.resume(throwing: NetworkError.invalidResponse) } } } } // 自定义错误类型(可选,根据你的需求定义) enum NetworkError: Error { case invalidResponse }
然后用Task来执行异步循环,等全部完成后跳转:
Task { var resultArray: [String] = [] for dataItem in yourDataSourceArray { do { // 等待当前请求完成,再执行下一次循环 let responseString = try await fetchDataAsync(for: dataItem) resultArray.append(responseString) } catch { // 处理请求错误,比如打印日志或者跳过当前项 print("Failed to fetch data for item: \(dataItem), error: \(error)") } } // 所有请求完成后,回到主线程执行Segue await MainActor.run { self.performSegue(withIdentifier: "YourSegueIdentifier", sender: resultArray) } }
最后:在目标控制器正确接收数据
别忘了在prepare(for:sender:)里安全地接收数组,避免nil崩溃:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "YourSegueIdentifier", let destinationVC = segue.destination as? YourTargetViewController, let receivedArray = sender as? [String] { // 把数组赋值给目标控制器的属性 destinationVC.dataArray = receivedArray } }
额外提醒:
- 用
[weak self]避免循环引用,防止内存泄漏 - 如果请求数量多,可以考虑用并发循环(比如
TaskGroup的async let)来提高效率,但要注意线程安全(数组追加要加锁或者用线程安全的容器)
内容的提问来源于stack exchange,提问作者Žan Fras
相关产品推荐
相关产品推荐

