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

iOS:从闭包调用UIAlertController时重复弹窗及崩溃问题咨询

解决登录界面重复弹出警告框的问题

这个问题我之前也碰到过,本质就是重复尝试在同一个视图控制器上弹出警告框导致的,咱们一步步拆解问题和解决办法:

问题根源分析

你看到的警告Attempt to present <UIAlertController: 0x7ff8fd0b5800> on <LoginViewController: 0x7ff8fcc0deb0> which is already presenting <UIAlertController: 0x7ff8fe0cca00>,核心原因有两个:

  • 登录请求被多次触发(比如用户快速点击登录按钮多次),导致回调闭包重复执行self.displayFailedLoginAlert();
  • 当第一个警告框还未关闭时,第二个警告框的present请求就已发起,而系统不允许同一个VC同时present多个控制器,因此抛出警告。

另外你的displayFailedLoginAlert里还有个小冗余:点击"Dismiss"按钮时手动调用alert.dismiss完全没必要——UIKit中,UIAlertAction默认会在点击后自动关闭对应的警告框,这段代码纯属画蛇添足。

解决方案

1. 从源头阻止重复登录请求

首先要确保同一时间只有一个登录请求在进行,避免多次触发回调。假设你的登录按钮点击事件是loginButtonTapped,修改逻辑如下:

@IBAction func loginButtonTapped(_ sender: UIButton) {
    // 检查是否正在登录,避免重复发起请求
    guard !loggingIn else { return }
    
    loggingIn = true
    _ = UserProfile.logIn(emailAddressLabel.text!, passwordLabel.text!) { success in 
        if success { 
            self.performSegue(withIdentifier: "mainTabBarSegue", sender: nil) 
        } else { 
            self.displayFailedLoginAlert() 
        }
        // 确保在主线程更新UI状态(如果logIn回调不在主线程)
        DispatchQueue.main.async {
            self.loggingIn = false
        }
    }
}

2. 确保每次只弹出一个警告框

修改displayFailedLoginAlert函数,在弹出前先检查当前VC是否已经在present其他控制器,如果是则跳过或处理现有警告:

func displayFailedLoginAlert() {
    // 若已有弹出的控制器,直接返回(也可根据需求更新现有警告内容)
    guard presentedViewController == nil else {
        return
    }
    
    let alert = UIAlertController(title: "Login", message: "Login Failed", preferredStyle: .alert)
    // 移除手动dismiss的代码,UIAlertAction默认会关闭警告
    alert.addAction(UIAlertAction(title: "Dismiss", style: .default))
    // 确保在主线程弹出警告(如果回调不在主线程)
    DispatchQueue.main.async {
        self.present(alert, animated: true, completion: nil)
    }
}

这里把animated设为true是更符合iOS交互规范的,如果你需要无动画效果也可以保持false。

3. 为什么把UIAlertController设为类成员会崩溃?

大概率是因为强引用循环:当你把alert设为类成员,同时alert的action handler里引用了self,就会形成self -> alert -> handler -> self的循环引用,导致内存无法释放,甚至在alert被dismiss后访问它时出现野指针崩溃。如果一定要用类成员管理alert,需要使用弱引用:

// 类成员
var loginAlert: UIAlertController?

func displayFailedLoginAlert() {
    if let existingAlert = loginAlert, existingAlert.isBeingPresented {
        return
    }
    
    let alert = UIAlertController(title: "Login", message: "Login Failed", preferredStyle: .alert)
    alert.addAction(UIAlertAction(title: "Dismiss", style: .default) { [weak self] _ in
        self?.loginAlert = nil
    })
    loginAlert = alert
    DispatchQueue.main.async {
        self.present(alert, animated: true)
    }
}

不过这种方式没必要,前面的两种方案已经足够解决问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:17:42