如何在不中断主线程的情况下延迟 swift 中的 for 循环?
How do I delay a for loop in swift without interrupting the main thread?
我试图在继续阅读下一个字符之前延迟 0.1 秒读取一个字符串中的一个字符。
我试过在for循环中实现延迟函数,但它有两个问题。
1. 延迟不一致,在角色之间移动时不会花费相同的时间。
2. 它扰乱了主线程,我认为这是导致相机视图冻结的原因。但是,我认为也有可能在相机开启时激活手电筒会冻结信号,从而导致故障。
func delay(_ delay:Double, closure:@escaping ()->()) {
DispatchQueue.main.asyncAfter(
deadline: DispatchTime.now () + Double(Int64(delay *
Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute:
closure)
}
func scriptReader(){
let str = "10101000001111110000110"
for i in 0..<str.count {
delay(Double(i) * 0.5) {
let index = str.index(str.startIndex, offsetBy: i)
self.flash(number: str[index])
}
}
}
func flash(number: Character){
guard let device = AVCaptureDevice.default(for:
AVMediaType.video) else { return }
guard device.hasTorch else { return }
if number == "0" {
print("off")
do {
try device.lockForConfiguration()
if (device.torchMode == AVCaptureDevice.TorchMode.on) {
device.torchMode = AVCaptureDevice.TorchMode.off
}
device.unlockForConfiguration()
} catch {
print(error)
}
}
if number == "1"{
print("on")
do {
try device.lockForConfiguration()
if (device.torchMode == AVCaptureDevice.TorchMode.off) {
do {
try device.setTorchModeOn(level: 1.0)
} catch {
print(error)
}
}
device.unlockForConfiguration()
} catch {
print(error)
}
}
}
我认为相机死机是因为你每次在for循环中调用它的属性,做一次,将它的属性传递给你的func
试试这个代码来解决你的问题
func scriptReader(){
let str = "10101000001111110000110"
guard let device = AVCaptureDevice.default(for: .video) else { return }
guard device.hasTorch else { return }
for i in 0..<str.count {
DispatchQueue.main.asyncAfter(deadline: .now() + Double(i) * 0.5) {
let index = str.index(str.startIndex, offsetBy: i)
self.flash(number: str[index])
}
}
}
func flash(device: AVCaptureDevice, isOn: Bool) {
do {
try device.lockForConfiguration()
device.torchMode = isOn ? AVCaptureDevice.TorchMode.off : device.setTorchModeOn(level: 1.0)
device.unlockForConfiguration()
} catch {
print(error)
}
}
关于您的第一个问题,使用一系列 asyncAfter
会受到“计时器合并”的影响,其中 OS 将对未来的、单独安排的计时器进行分组以同时触发所有计时器, 以充分利用设备电池(OS 唤醒设备的频率越低,电池寿命越长)。计划的计时器越远,OS 的合并就越多。
可以通过使用重复 Timer
:
来避免这种情况
func handle(string: String) {
guard !string.isEmpty else { return }
var index = string.startIndex
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in
if string[index] == "0" {
// handle "0"
} else {
// handle not "0"
}
index = string.index(after: index)
if index == string.endIndex { timer.invalidate() }
}
}
像这样简单的方法也可能有效:
let threadName = "FlashThread"
let string = "10101000001111110000110"
//Create a custom thread
DispatchQueue(label: threadName).async {
print("Thread Start")
//Start loop
string.forEach { (c) in
print(c)
//Do something on main thread
DispatchQueue.main.async {
self.flash(number: c)
}
//Put your thread to sleep for a second
sleep(1)
}
print("Thread END")
}
我现在已经解决了这个问题。第一个问题用 Rob 提供的定时器代码解决了。这停止了主线程的冻结,现在代码以一致的时序遍历字符串。
第二个问题通过消除 AVCaptureDevice
的多个实例得到修复,一旦修复,代码就可以工作了。
感谢您的帮助!
我试图在继续阅读下一个字符之前延迟 0.1 秒读取一个字符串中的一个字符。
我试过在for循环中实现延迟函数,但它有两个问题。 1. 延迟不一致,在角色之间移动时不会花费相同的时间。 2. 它扰乱了主线程,我认为这是导致相机视图冻结的原因。但是,我认为也有可能在相机开启时激活手电筒会冻结信号,从而导致故障。
func delay(_ delay:Double, closure:@escaping ()->()) {
DispatchQueue.main.asyncAfter(
deadline: DispatchTime.now () + Double(Int64(delay *
Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute:
closure)
}
func scriptReader(){
let str = "10101000001111110000110"
for i in 0..<str.count {
delay(Double(i) * 0.5) {
let index = str.index(str.startIndex, offsetBy: i)
self.flash(number: str[index])
}
}
}
func flash(number: Character){
guard let device = AVCaptureDevice.default(for:
AVMediaType.video) else { return }
guard device.hasTorch else { return }
if number == "0" {
print("off")
do {
try device.lockForConfiguration()
if (device.torchMode == AVCaptureDevice.TorchMode.on) {
device.torchMode = AVCaptureDevice.TorchMode.off
}
device.unlockForConfiguration()
} catch {
print(error)
}
}
if number == "1"{
print("on")
do {
try device.lockForConfiguration()
if (device.torchMode == AVCaptureDevice.TorchMode.off) {
do {
try device.setTorchModeOn(level: 1.0)
} catch {
print(error)
}
}
device.unlockForConfiguration()
} catch {
print(error)
}
}
}
我认为相机死机是因为你每次在for循环中调用它的属性,做一次,将它的属性传递给你的func
试试这个代码来解决你的问题
func scriptReader(){
let str = "10101000001111110000110"
guard let device = AVCaptureDevice.default(for: .video) else { return }
guard device.hasTorch else { return }
for i in 0..<str.count {
DispatchQueue.main.asyncAfter(deadline: .now() + Double(i) * 0.5) {
let index = str.index(str.startIndex, offsetBy: i)
self.flash(number: str[index])
}
}
}
func flash(device: AVCaptureDevice, isOn: Bool) {
do {
try device.lockForConfiguration()
device.torchMode = isOn ? AVCaptureDevice.TorchMode.off : device.setTorchModeOn(level: 1.0)
device.unlockForConfiguration()
} catch {
print(error)
}
}
关于您的第一个问题,使用一系列 asyncAfter
会受到“计时器合并”的影响,其中 OS 将对未来的、单独安排的计时器进行分组以同时触发所有计时器, 以充分利用设备电池(OS 唤醒设备的频率越低,电池寿命越长)。计划的计时器越远,OS 的合并就越多。
可以通过使用重复 Timer
:
func handle(string: String) {
guard !string.isEmpty else { return }
var index = string.startIndex
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in
if string[index] == "0" {
// handle "0"
} else {
// handle not "0"
}
index = string.index(after: index)
if index == string.endIndex { timer.invalidate() }
}
}
像这样简单的方法也可能有效:
let threadName = "FlashThread"
let string = "10101000001111110000110"
//Create a custom thread
DispatchQueue(label: threadName).async {
print("Thread Start")
//Start loop
string.forEach { (c) in
print(c)
//Do something on main thread
DispatchQueue.main.async {
self.flash(number: c)
}
//Put your thread to sleep for a second
sleep(1)
}
print("Thread END")
}
我现在已经解决了这个问题。第一个问题用 Rob 提供的定时器代码解决了。这停止了主线程的冻结,现在代码以一致的时序遍历字符串。
第二个问题通过消除 AVCaptureDevice
的多个实例得到修复,一旦修复,代码就可以工作了。
感谢您的帮助!