Swift项目求助:shouldPerformSegue中doApi()执行顺序异常
问题根源
你遇到的是典型的异步代码执行顺序问题——URLSession.dataTask是异步执行的网络请求,当你在shouldPerformSegue里调用doApi()时,方法会立刻启动请求,然后直接返回初始的check = false,而请求完成后设置check = true的逻辑要等到网络请求结束才会运行,这就导致你的打印语句先于API回调执行。
而且shouldPerformSegue是系统要求同步返回结果的方法,没法在这里等待异步操作完成,所以得换个思路处理。
解决方案
我们可以把Segue改成手动触发,步骤如下:
1. 先修改Storyboard设置
- 删掉登录按钮和目标控制器之间的自动触发Segue(或者把Segue的触发方式从按钮点击改为手动)
- 保留Segue的
identifier为performSegueLogin不变
2. 修改代码逻辑
把API请求放到doLogin方法里,等异步请求完成后,再手动决定是否执行Segue:
class ViewController: UIViewController { var loginArr = [String]() @IBOutlet weak var _output: UILabel! @IBOutlet weak var _username: UITextField! @IBOutlet weak var _password: UITextField! @IBAction func doLogin(_ sender: Any) { loginArr.removeAll() guard let username = _username.text, !username.isEmpty, let password = _password.text, !password.isEmpty else { // 提示用户输入不能为空 _output.text = "用户名或密码不能为空" return } loginArr.append(username) loginArr.append(password) self._output.text = username // 调用API验证登录 doApi { [weak self] isSuccess in guard let self = self else { return } if isSuccess { print("2 - true") // 验证通过,手动执行Segue self.performSegue(withIdentifier: "performSegueLogin", sender: self) } else { print("1 - false") // 验证失败,更新UI提示用户 self._output.text = "登录失败,请检查账号密码" } } } // 修改doApi为带回调的异步方法,不再直接返回Bool func doApi(completion: @escaping (Bool) -> Void){ let headers = [ "cache-control": "no-cache", "postman-token": "6f8a-12c6-87a1-ac0f25d6385a" ] guard let username = _username.text, let password = _password.text, let url = URL(string: "https://projects2018.sz-ybbs.ac.at/~szmed/indyapp/indyapi.php?func=0&user=\(username)&pass=\(password)") else { completion(false) return } var request = URLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request) { (data, response, error) -> Void in var loginSuccess = false defer { // 回到主线程执行回调(UI操作必须在主线程) DispatchQueue.main.async { completion(loginSuccess) } } if let error = error { print("网络请求出错:\(error.localizedDescription)") return } guard let data = data else { print("未获取到返回数据") return } do { if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any] { print(json) // 这里建议根据API实际返回的状态判断登录是否成功,比如json["success"] == true loginSuccess = true } } catch { print("JSON解析失败:\(error.localizedDescription)") } }.resume() } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "performSegueLogin" { let DashboardC = segue.destination as! DashboardController DashboardC.receivedStringArr = loginArr } } // 不再需要shouldPerformSegue,因为我们手动控制Segue触发时机 }
关键改动说明
- 将
doApi改成带completion回调的异步方法,确保能在网络请求完成后通知调用方结果 - 在
doLogin中调用doApi,根据回调结果手动触发Segue或提示错误 - 添加
guard语句处理空值,避免强制解包导致崩溃 - 网络请求回调中用
DispatchQueue.main.async切回主线程,保证UI操作的安全性 - 移除
shouldPerformSegue方法,改为手动控制Segue触发
额外建议
- 不要依赖强制解包(比如
_username.text!),用guard或if let处理空值,提升代码稳定性 - 登录成功的判断要结合API实际返回的字段,比如API返回
"status": "success"才判定为登录成功,而不是仅判断请求是否成功 - 可以添加加载动画(比如
UIActivityIndicatorView),让用户感知到登录请求正在进行
内容的提问来源于stack exchange,提问作者Pheeerox
相关产品推荐
相关产品推荐

