Swift中Spinner在队列任务完成前提前消失的问题求助
兄弟,我太懂这种被异步逻辑坑到抓狂的感觉了😅——你明明想等所有DCS BIOS的消息都发送完再把Spinner去掉,结果它早早消失,任务还在后台跑,完全不符合预期。
问题根源
你代码里的biosQueue.async { dcsBIOSConn.sendMessageQueueToDCSBIOS() }是非阻塞异步操作:当你把这个任务扔进biosQueue后,当前的utility队列不会等它执行完,直接跳过继续往下走,立刻就调用了DispatchQueue.main.async去移除Spinner。这就是为什么打印顺序是1→2→3,而发送消息的日志在最后才出现——Spinner的移除逻辑根本没等发送任务完成。
两种可行解决方案
方案一:用同步执行阻塞当前队列
把biosQueue.async改成biosQueue.sync,这样当前的utility队列会暂停,直到sendMessageQueueToDCSBIOS()完全执行完毕,再继续后续的主线程Spinner移除操作:
DispatchQueue.global(qos: .utility).async { [weak self] in guard let self = self else { return } switch airframe{ case .A_10: dcsBIOSConn.configureHawgWaypoint(name: name, lat: latitude!, long: longitude!, elev: elev, tot: timeOnTarget) case .F_16, .F_18, .AV_8B: dcsBIOSConn.configureWaypoint(lat: latitude!, long: longitude!, elev: elev, aircraft: airframe!) default: break } dcsBIOSConn.printMessageQueue() print("1. Entering Queue") let biosQueue = DispatchQueue(label: "dcs_bios") // 改成sync,让当前队列等待biosQueue的任务完成 biosQueue.sync { dcsBIOSConn.sendMessageQueueToDCSBIOS() print("任务发送完成") } print("2. Finishing Out of Loop") // 此时发送任务已完成,再更新UI DispatchQueue.main.async { [weak self] in self?.removeSpinner() print("3. Remove Spinner") } }
这种方案的打印顺序会变成:1→任务发送完成→2→3,完全符合你想要的“任务完成后再移除Spinner”的逻辑。
方案二:在发送任务内部调度主线程
如果不想阻塞utility队列(比如后续还有其他任务要执行),可以把移除Spinner的逻辑直接放到biosQueue.async的闭包末尾,等发送任务完成后再回到主线程更新UI:
DispatchQueue.global(qos: .utility).async { [weak self] in guard let self = self else { return } switch airframe{ case .A_10: dcsBIOSConn.configureHawgWaypoint(name: name, lat: latitude!, long: longitude!, elev: elev, tot: timeOnTarget) case .F_16, .F_18, .AV_8B: dcsBIOSConn.configureWaypoint(lat: latitude!, long: longitude!, elev: elev, aircraft: airframe!) default: break } dcsBIOSConn.printMessageQueue() print("1. Entering Queue") let biosQueue = DispatchQueue(label: "dcs_bios") biosQueue.async { dcsBIOSConn.sendMessageQueueToDCSBIOS() print("任务发送完成") // 发送完成后,直接调度主线程移除Spinner DispatchQueue.main.async { [weak self] in self?.removeSpinner() print("3. Remove Spinner") } } print("2. Finishing Out of Loop") }
这种方案的打印顺序会是1→2→任务发送完成→3,虽然2. Finishing Out of Loop会提前打印,但Spinner的移除时机是正确的——只有等发送任务完成后才会执行。
额外提醒
要确保sendMessageQueueToDCSBIOS()方法是线程安全的,因为现在它是在biosQueue这个独立队列里执行的,避免出现多线程资源竞争的问题。
内容的提问来源于stack exchange,提问作者MonkeyBusiness

