gpt4 book ai didi

macos - Mac 应用程序窗口停止更新

转载 作者:可可西里 更新时间:2023-11-01 00:53:48 25 4
gpt4 key购买 nike

我正在 Swift 中编写一个 Mac 应用程序(目标 10.9+,在 Mavericks 上使用 Xcode 6 Beta 3),其中我有许多 NSTextField(标签)在较长时间内每秒更新几次通过从后台线程修改它们的 .stringvalue 来节省时间。这似乎适用于不同的持续时间(5 分钟到 2 小时之间的任何时间),但随后应用程序窗口似乎停止更新。文本停止更新,将鼠标悬停在左上角的“红绿灯”控件上不会显示符号,单击文本框等不会突出显示框/显示工字梁。但是,我不确定的进度轮会继续旋转,当我调整/最小化/缩放窗口,或在 NSScrollView 框中滚动时,窗口会在移动过程中更新。

我的第一个猜测是使用了某种窗口缓冲区而不是实时图像,所以我尝试使用 window.update()window.flushWindowIfNeeded( )window.flushWindow(),都无济于事。谁能告诉我这是怎么回事,为什么我的窗口停止更新,以及如何解决这个问题?

最佳答案

你的问题就在这里:

I have a number of NSTextFields (labels) updating several times per second for extended periods of time by modifying their .stringvalue from a background thread.

在 OSX(和 iOS)中,UI 更新必须发生在主线程/队列中。否则就是未定义的行为;有时它会工作,有时它不会,有时它会崩溃。

快速解决问题的方法是简单地使用 Grand Central Dispatch (GCD) 使用 dispatch_async 将这些更新分派(dispatch)到主队列喜欢:

dispatch_async(dispatch_get_main_queue(), ^{
textField.stringValue = "..."
});

该操作的简化版本是将 block /闭包({} 之间的代码)放入默认运行循环(在主线程/队列上运行)检查的队列中每次通过它的循环。当运行循环在队列中看到一个新 block 时,它会将它弹出并执行它。此外,由于使用的是 dispatch_async(与 dispatch_sync 相对),执行分派(dispatch)的代码不会阻塞; dispatch_async 将 block 排队并立即返回。

注意:如果您还没有读过 GCD,我强烈建议您看一下 this链接和上面的引用链接(this 也是一个关于 OSX/iOS 中涉及 GCD 的通用并发的好链接)。

使用计时器减轻 UI 的压力

编辑: 每秒几次确实不算多,所以这部分可能有点过分了。但是,如果您每秒超过 30-60 次,那么它将变得相关。

您不希望遇到这样一种情况,即您排队等待 UI 更新积压的速度快于它们的处理速度。在这种情况下,使用计时器更新 NSTextField 会更有意义。

基本思想是将您希望在 NSTextField 中显示的值存储在某处的某个中间变量中。然后,启动一个定时器,在主线程/队列的十分之一秒左右触发一个函数。在该函数中,使用存储在该中间变量中的值更新您的 NSTextField。由于计时器已经在主线程/队列上运行,因此您已经在正确的位置进行 UI 更新。

我会使用 NSTimer设置定时器。它看起来像这样:

var timer: NSTimer?

func startUIUpdateTimer() {
// NOTE: For our purposes, the timer must run on the main queue, so use GCD to make sure.
// This can still be called from the main queue without a problem since we're using dispatch_async.
dispatch_async(dispatch_get_main_queue()) {
// Start a time that calls self.updateUI() once every tenth of a second
timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target:self, selector:"updateUI", userInfo: nil, repeats: true)
}
}

func updateUI() {
// Update the NSTextField(s)
textField.stringValue = variableYouStoredTheValueIn
}

注意:正如@a​​dv12 指出的,当您从多个线程访问相同数据时,您应该考虑数据同步。

注意:您还可以使用 dispatch sources 将 GCD 用于计时器, 但 NSTimer 更易于使用 ( see here if interested )。

使用这样的计时器应该可以使您的 UI 保持快速响应;无需担心“让主线程尽可能空”。如果出于某种原因,您开始失去一些响应能力,只需更改计时器,使其不再那么频繁地更新。


更新:数据同步

正如 @adv12 所指出的,如果您在后台线程上更新数据,然后使用它来更新主线程中的 UI,则应该同步您的数据访问。实际上,您可以使用 GCD 通过创建一个串行队列并确保您只读/写分派(dispatch)到该队列的 block 中的数据来相当容易地做到这一点。由于串行队列一次只执行一个 block ,按照接收 block 的顺序,它保证只有一个代码块将同时访问您的数据。

设置你的串行队列:

let dataAccessQueue = dispatch_queue_create("dataAccessQueue", DISPATCH_QUEUE_SERIAL)

围绕您的读写:

dispatch_sync(dataAccessQueue) {
// do reads and/or writes here
}

关于macos - Mac 应用程序窗口停止更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25168847/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com