You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift 4.1中POST请求获取Token时方法返回值异常问题

问题分析与解决方案:异步网络请求导致返回值异常

嘿,我一眼就看出问题所在了——你遇到了异步代码的经典陷阱!

问题根源

URLSession.shared.dataTask(with:) 是一个异步执行的网络请求。当你调用 task.resume() 后,这个请求会在后台线程默默执行,而主线程不会停下来等它完成,会直接继续往下走,执行 return funcResult。这时候 funcResult 还是你初始化的 false,等网络请求成功拿到 Token 并把 funcResult 改成 true 的时候,方法早就已经返回了,所以你永远拿到的是 false。

调试时看到代码从 let task = 直接跳到 task.resume() 然后返回,就是因为主线程不会等待异步任务完成,直接执行后续代码了。

解决方案:用闭包回调代替同步返回值

异步操作不能用同步返回值的方式来获取结果,最常用的解决办法是使用闭包回调。我们修改一下你的代码:

首先重构 getTokenWithPostMethod 方法,把返回值换成闭包:

func getTokenWithPostMethod(completion: @escaping (Bool, String?) -> Void) {
    guard let url = URL(string: "https://apiABCDEFG.com/token") else {
        completion(false, nil)
        return
    }
    
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    let postString = "grant_type=password&username=ABCDEF&password=ABCDEF"
    request.httpBody = postString.data(using: .utf8)
    request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")
    
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        // 处理网络错误
        guard let data = data, error == nil else {
            print("error=\(String(describing: error))")
            completion(false, nil)
            return
        }
        
        // 检查HTTP状态码
        if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {
            print("statusCode should be 200, but is \(httpStatus.statusCode)")
            print("response = \(String(describing: response))")
            completion(false, nil)
            return
        }
        
        // 解析JSON
        do {
            if let jsonObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any] {
                let token = jsonObject["access_token"] as? String
                let tokenType = jsonObject["token_type"] as? String
                
                print(token ?? "")
                print("\n")
                print(tokenType ?? "")
                
                // 请求成功,通过闭包返回结果
                completion(true, token)
            } else {
                completion(false, nil)
            }
        } catch let error as NSError {
            print(error)
            completion(false, nil)
        }
    }
    task.resume()
}

然后修改按钮点击的处理函数,通过闭包接收结果并更新UI:

@IBAction func btnHitWebServiceClick(_ sender: Any) {
    getTokenWithPostMethod { [weak self] success, token in
        // 注意:网络回调在后台线程,更新UI必须切回主线程
        DispatchQueue.main.async {
            guard let self = self else { return }
            if success, let token = token {
                self.lblResult.text = "yes, we get token: " + token
            } else {
                self.lblResult.text = "there is an error, please try again later"
            }
        }
    }
}

额外提示

  • 避免全局变量:你原来用的 g_token 和 g_token_type 尽量不要用,通过闭包传递数据更安全,也符合Swift的编码规范。
  • 防止循环引用:用 [weak self] 捕获self,避免ViewController因为网络任务的闭包而无法被释放,造成内存泄漏。
  • UI更新必须在主线程:所有UI操作都要放在主线程执行,所以我们用了 DispatchQueue.main.async 包裹UI更新代码。

这样修改后,当网络请求完成时,闭包会被触发,你就能正确拿到Token并更新标签内容了。

内容的提问来源于stack exchange,提问作者mannyCalavera

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:18:48