Swift弹窗与Dispatch Async调用时序异常:原因及解决方案求助
问题原因分析与解决方案
嘿,这个问题我刚学Swift的时候也踩过类似的坑,我来给你捋清楚到底咋回事,以及该怎么修复~
为什么当前代码的时序不对?
你现在的代码逻辑里,DispatchQueue.main.async并没有起到你想要的“等待弹窗操作完成”的作用,原因有两个:
DispatchQueue.main.async只是把弹窗的present任务放到主队列的下一个执行周期,但它不会阻塞当前线程——也就是说,print("third")会立刻执行,根本不会等弹窗加载完成,更不会等用户点击按钮。- 你要等的是用户点击弹窗按钮这个异步交互事件,这完全不是用队列调度能解决的,队列只能管理代码执行的先后顺序,但管不了用户什么时候点击按钮。
所以当前的执行顺序是:
- 先打印
first - 把
present弹窗的任务丢到主队列(但当前线程继续往下走) - 立刻打印
third - 主队列空闲时执行
present,弹窗加载后调用viewDidLoad打印second - 之后用户点击按钮,才会执行
Slot1里的代码,但这时候third早就打印完了
正确的解决方案:用闭包回调实现等待
要实现“等新弹窗按钮被点击后再执行主视图操作”,最直接的方式是给弹窗VC添加一个回调闭包,当用户点击按钮并关闭弹窗后,触发这个闭包来执行后续操作。
第一步:修改弹窗VC的代码
给P2_Competition_Pop_Up添加一个闭包属性,在按钮点击关闭弹窗的回调里触发这个闭包:
class P2_Competition_Pop_Up: UIViewController { // 定义一个可选闭包,用来传递按钮点击事件 var onSlotButtonTapped: (() -> Void)? override func viewDidLoad() { super.viewDidLoad() print("Second") } @IBAction func Slot1(_ sender: Any) { // 先执行你原来的some code // ... // 关闭弹窗后,调用回调闭包 dismiss(animated: true) { [weak self] in self?.onSlotButtonTapped?() } } }
第二步:修改主VC的onPopupClosed方法
在present弹窗的时候,给这个闭包赋值,把你原来的print("third")(也就是修改主视图外观的代码)放到闭包里:
@objc func onPopupClosed() { print("first") // 其实这里的DispatchQueue.main.async可以去掉,因为UI操作本来就该在主队列执行,而onPopupClosed一般也是在主队列触发的 if let vc = self.storyboard?.instantiateViewController(withIdentifier: "P2CompetitionPopUpId") as? P2_Competition_Pop_Up { vc.modalPresentationStyle = .overCurrentContext // 设置回调:当弹窗按钮被点击后,执行这里的代码 vc.onSlotButtonTapped = { [weak self] in print("third") // 这里放你原来要修改主视图外观的代码 } self.present(vc, animated: true, completion: nil) } else { print("error creating P2_Competion_Pop_Up") } }
这样修改后的时序就对了:
- 第一个弹窗关闭,触发
onPopupClosed,打印first - present第二个弹窗,弹窗加载完成后
viewDidLoad打印second - 用户点击
Slot1按钮,弹窗关闭,触发onSlotButtonTapped闭包,打印third并执行主视图的修改操作
额外注意点:
- 用
[weak self]是为了避免循环引用:主VC持有弹窗VC,弹窗VC的闭包又持有主VC,弱引用可以打破这个循环,防止内存泄漏。 - 除了闭包,你也可以用代理模式来实现同样的效果,但闭包写法更简洁,适合这种简单的回调场景。
内容的提问来源于stack exchange,提问作者ProfessorHelloKittyNr1
相关产品推荐
相关产品推荐

