DispatchQueue.main.async 挂起后重复,但使用睡眠时不挂起
Posted
技术标签:
【中文标题】DispatchQueue.main.async 挂起后重复,但使用睡眠时不挂起【英文标题】:DispatchQueue.main.asyncAfter hanging on repeat, but does not hang when using sleep 【发布时间】:2021-11-28 13:53:04 【问题描述】:我正在尝试使用 Swift 为 Macos 创建一个机器人流程自动化工具。用户创建一个自动化,它是一系列 Step 对象,然后播放它。 Step 的子类之一是 Pause,它应该暂停执行给定的秒数。
由于某种原因,当我在 Pause 类中使用 DispatchQueue.main.asyncAfter()
方法时,执行挂起。通常第一次运行自动化是好的,但是当它重复时,它最终会挂起更长的时间。当我改用sleep()
时,错误消失了。
关于这个错误的另一个奇怪的事情是,当我打开 Xcode 尝试查看发生了什么时,挂起解决并继续执行。我想知道该进程是否以某种方式进入后台,然后 DispatchQueue.main.asyncAfter()
不起作用。我尝试将 Info.plist “应用程序不在后台运行”更改为 YES,但这没有任何效果。
使用sleep()
的问题是它会阻塞 UI 线程,因此用户无法在需要时停止自动化。我用 DispatchQueue 尝试了许多不同的线程变体,但它似乎总是在重复执行时挂在某个地方。我也尝试过使用 Timer.scheduledTimer()
而不是 DispatchQueue 但这也挂起。我确定我错过了一些简单的东西,但我无法弄清楚。
创建步进数组并启动自动化
class AutomationPlayer
static let shared = AutomationPlayer()
var automation: Automation?
var stepArray: [Step] = []
func play()
// Create array of steps
guard let steps = automation?.steps, let array = Array(steps) as? [Step] else
return
// If the automation repeats, add more steps to array.
for _ in 0..<(automation?.numberOfRepeats ?? 1)
for (index, step) in array.enumerated()
stepArray.append(step)
// Add small delay to allow window to close before execution.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) [weak self] in
self?.execute(index: 0)
private func execute(index: Int)
let step = stepArray[index]
executeStep(step: step) [weak self] success, error in
guard error == nil else return
let newIndex = index + 1
if newIndex < self?.stepArray.count ?? 0
//Need a small delay between steps otherwise execution is getting messed up.
usleep(400000)
self?.execute(index: newIndex)
else
self?.stepArray = []
private func executeStep(step: Step?, completionHandler: @escaping (Bool, Error?) -> Void) -> Void
step?.execute(completionHandler: [weak self] success, error in
guard error == nil else
completionHandler(false, error)
return
completionHandler(true, nil)
)
暂停课
@objc(Pause)
public class Pause: Step
override func execute(completionHandler: @escaping (Bool, Error?) -> Void)
print("Pause for: \(self.time) seconds")
// This will eventually hang when the automation repeats itself
DispatchQueue.main.asyncAfter(deadline: .now() + Double(self.time))
completionHandler(true, nil)
)
// This will also hang
Timer.scheduledTimer(withTimeInterval: self.time, repeats: false) timer in
completionHandler(true, nil)
// If I use this instead, the automation repeats just fine
sleep(UInt32(self.time))
completionHandler(true, nil)
【问题讨论】:
我尝试使用 Timer,但在第一次重复后它仍然挂起。 这里发生了很多非常时髦的事情。我建议您将其交叉发布到 codereview.stackexchange.com 【参考方案1】:所以我想我想通了。 MacOS 在一段时间后将我的应用程序放入 AppNap,这将导致 DispatchQueue.main.async()
停止工作。由于某些原因,AppNap 不会影响您使用sleep()
时的延迟
我找到了答案here
这个答案有点老了。我正在使用 SwiftUI 构建我的 mac 应用程序,所以我添加了这个我的 @main 结构
@main
struct Main_App: App
@State var activity: NSObjectProtocol?
var body: some Scene
WindowGroup("")
MainWindow()
.onAppear
activity = ProcessInfo().beginActivity(options: .userInitiated, reason: "Good Reason")
这似乎阻止了应用程序进入 AppNap 并且自动化继续进行。这很丑陋,但它确实有效。
【讨论】:
以上是关于DispatchQueue.main.async 挂起后重复,但使用睡眠时不挂起的主要内容,如果未能解决你的问题,请参考以下文章
为啥 DispatchQueue.main.async 会影响单元格自动调整大小?
SwiftUI - HealthKit 中的 DispatchQueue.main.async
SwiftUI - HealthKit 中的 DispatchQueue.main.async
Swift 5:我无法让我的 UITableView 及时更新(同时使用 `DispatchQueue.global().sync` 和 `DispatchQueue.main.async`