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

Swift 4.1异步调用Web API异常:首次点击失败二次成功的修复

修复Swift中Web API调用的异步顺序问题

这是个很典型的异步执行顺序问题,我来帮你分析并解决它:

问题根源

你遇到的核心问题是异步代码的执行时机:callMyApi()里的URLSession.dataTask(with:)是异步执行的网络请求——它会在后台线程发起请求,不会阻塞主线程。当你在按钮点击事件里调用callMyApi()后,立刻就去判断globalTempValue的值,这时候网络请求还没完成,globalTempValue还是初始的空字符串,所以第一次点击会输出fail 998。而第二次点击时,第一次的网络请求已经完成,globalTempValue被赋值成了999,所以会输出success 999。

你之前添加的DispatchQueue.main.async只是把赋值操作切回了主线程,但并没有改变"判断逻辑先于请求完成"的顺序,所以无法解决问题。

解决方案:使用闭包(Completion Handler)处理异步回调

最直接的修复方式是给callMyApi()添加一个完成回调闭包,让请求完成后再执行判断和后续逻辑。这样就能保证代码的执行顺序符合预期。

修改后的完整代码如下:

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var lblDetail: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    struct MyGitHub: Codable {
        let resultCode: String?
        
        private enum CodingKeys: String, CodingKey {
            case resultCode
        }
    }
    
    @IBAction func btnClickAction(_ sender: Any) {
        callMyApi { [weak self] resultCode in
            guard let self = self else { return }
            // 这里的逻辑会在网络请求完成后执行
            if resultCode == "999" {
                print("success 999")
                // 还可以在这里更新UI,比如给lblDetail赋值
                self.lblDetail.text = "请求成功"
            } else {
                print("fail 998")
                self.lblDetail.text = "请求失败"
            }
        }
    }
    
    func callMyApi(completion: @escaping (String?) -> Void) {
        guard let gitUrl = URL(string: "https://transsupp.com/tApp/ws01.ashx") else {
            completion(nil)
            return
        }
        
        URLSession.shared.dataTask(with: gitUrl) { data, response, error in
            guard let data = data, error == nil else {
                print("网络请求错误:\(error?.localizedDescription ?? "未知错误")")
                completion(nil)
                return
            }
            
            do {
                let decoder = JSONDecoder()
                let gitData = try decoder.decode(MyGitHub.self, from: data)
                // 把解析后的结果通过闭包传递出去
                DispatchQueue.main.async {
                    completion(gitData.resultCode)
                }
            } catch let err {
                print("解析数据错误:\(err)")
                completion(nil)
            }
        }.resume()
    }
}

关键修改点说明

  1. 添加Completion Handler:给callMyApi方法增加了completion: @escaping (String?) -> Void参数,用来在请求完成后传递结果。
  2. 调整执行顺序:按钮点击事件里,把原来的判断逻辑放到了callMyApi的闭包回调中,这样逻辑会在网络请求完成并解析数据后才执行。
  3. 安全解包:去掉了强制解包gitData.resultCode!,改用可选值传递,避免因接口返回null导致崩溃。
  4. 弱引用self:在闭包中使用[weak self]避免循环引用,保证内存安全。

这样修改后,首次点击按钮时,网络请求完成后就会立刻执行判断逻辑,输出success 999,完全符合你的预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:15:29