Swift中DataTask的完成处理程序为何始终不执行?
问题原因
在Swift命令行程序中,主线程的RunLoop默认不会持续运行。URLSession.dataTask是异步执行的,当main函数执行完毕后,进程会立即退出,根本没有等待网络任务完成并触发回调。这就是你看不到任何任务相关输出、程序直接显示Finished with exit code 0的原因。
解决方案
下面提供几种可行的修复方式,你可以根据需求选择:
方法1:使用信号量(DispatchSemaphore)阻塞主线程
通过信号量让主线程等待异步任务完成后再退出:
修改main.swift代码:
import Foundation let urlString = "https://api.edamam.com/api/food-database/v2/parser?app_id=4c38caa6&app_key=kkk&ingr=eggs 4 small" guard let encodedUrlString = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let url = URL(string: encodedUrlString) else { fatalError("Invalid URL") } // 创建信号量,初始值为0 let semaphore = DispatchSemaphore(value: 0) Requests.makeGet(url: url) { d, e in print("Inside closure from main") print("Data: \(d ?? Data())") print("Error: \(e ?? "No error")") // 任务完成,发送信号让主线程继续执行 semaphore.signal() } // 阻塞主线程,等待信号量 semaphore.wait()
方法2:使用RunLoop保持程序运行
让主线程的RunLoop持续运行,直到异步任务完成:
修改main.swift代码:
import Foundation let urlString = "https://api.edamam.com/api/food-database/v2/parser?app_id=4c38caa6&app_key=kkk&ingr=eggs 4 small" guard let encodedUrlString = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let url = URL(string: encodedUrlString) else { fatalError("Invalid URL") } var shouldKeepRunning = true Requests.makeGet(url: url) { d, e in print("Inside closure from main") print("Data: \(d ?? Data())") print("Error: \(e ?? "No error")") // 任务完成,标记退出RunLoop shouldKeepRunning = false } // 持续运行RunLoop直到任务完成 while shouldKeepRunning { RunLoop.main.run(until: Date().addingTimeInterval(0.1)) }
方法3:使用Async/Await(Swift 5.5+)
如果你的Swift版本支持Async/Await,这种方式代码更简洁易读,也不需要手动管理线程阻塞:
首先更新Requests结构体:
import Foundation struct Requests { static func makeGet(url: URL) async throws -> Data { let (data, response) = try await URLSession.shared.data(from: url) // 可选:检查HTTP响应状态码 guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else { throw NSError(domain: "RequestError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid HTTP response"]) } return data } }
然后修改main.swift:
import Foundation // 启动异步任务 Task { let urlString = "https://api.edamam.com/api/food-database/v2/parser?app_id=4c38caa6&app_key=kkk&ingr=eggs 4 small" guard let encodedUrlString = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let url = URL(string: encodedUrlString) else { fatalError("Invalid URL") } do { let data = try await Requests.makeGet(url: url) print("Received data: \(data)") } catch { print("Request failed with error: \(error)") } // 任务完成后退出程序 exit(0) } // 保持RunLoop运行,等待异步任务完成 RunLoop.main.run()
内容的提问来源于stack exchange,提问作者Daniel Crompton
相关产品推荐
相关产品推荐

