Swift 5命令行调用REST API无结果,dataTask闭包未执行求助
解决Swift命令行程序中URLSession异步任务未执行的问题
你遇到的核心问题是命令行程序的主线程在异步网络任务完成前就直接退出了。Playground会自动等待所有异步任务执行完毕,但命令行程序的运行逻辑不一样——当callRest()执行完task.resume()后,主线程会立刻继续执行print("fin"),之后程序就直接结束了,根本没给网络请求的闭包留执行的机会。
为什么会这样?
URLSession的dataTask(with:completionHandler:)是异步执行的:它会在后台线程发起网络请求,等请求完成后才会调用你传入的闭包。但命令行程序的生命周期是从启动到主线程代码执行完毕就结束,不会主动等待后台的异步任务。
怎么解决?
最适合命令行场景的方案是用**信号量(Semaphore)**阻塞主线程,强制等待异步任务完成后再继续。下面是修改后的代码:
func callRest() { URLCache.shared = URLCache(memoryCapacity: 0, diskCapacity: 0, diskPath: nil) let todoEndpoint: String = "http://myURL" print(todoEndpoint) guard let url = URL(string: todoEndpoint) else { print("Error: cannot create URL") exit(1) } let config = URLSessionConfiguration.default config.httpAdditionalHeaders = [ "Accept": "application/json", "API-Key": "my API Key" ] // 简化session初始化,没必要先赋值shared再替换 let session = URLSession(configuration: config) let urlRequest = URLRequest(url: url) // 创建信号量,初始值为0(表示当前无可用资源) let semaphore = DispatchSemaphore(value: 0) let task = session.dataTask(with: urlRequest) { (data, response, error) in print("task") // 错误处理逻辑不变,每一个分支结束前都要发送信号 guard error == nil else { print("error calling GET on /todos/1") print(error!) semaphore.signal() // 通知主线程任务完成 return } guard let responseData = data else { print("Error: did not receive data") semaphore.signal() return } guard let httpResponse = response as? HTTPURLResponse else { print("Error: It's not a HTTP URL response") semaphore.signal() return } print(httpResponse) semaphore.signal() // 正常完成后发送信号 } task.resume() // 阻塞主线程,直到信号量被唤醒 semaphore.wait() } print("debut") callRest() print("fin")
代码说明
- 信号量
semaphore初始值为0,调用wait()时会让主线程暂停,直到signal()被调用 - 无论网络请求成功还是失败,闭包的每一个分支都调用
semaphore.signal(),确保主线程能被唤醒继续执行 - 这样就能保证网络任务的闭包有足够时间执行,你就能看到
print("task")和其他输出了
额外注意
- 这种信号量阻塞的方式仅适合命令行场景,绝对不能在iOS/macOS的UI程序中使用——会阻塞主线程导致界面卡死
- 你原来代码里
var session = URLSession.shared再重新赋值的写法可以简化,直接用let session = URLSession(configuration: config)即可
内容的提问来源于stack exchange,提问作者Olivier Pieri
相关产品推荐
相关产品推荐

