How do I delay a for loop in swift without interrupting the main thread?

左心房为你撑大大i 提交于 2021-01-28 06:47:05

问题


I am trying to read through a string one character a time delaying .1 seconds before moving on to the next character.

I have tried implementing the delay function inside the for loop but it has two problems. 1. The delay is inconsistent and does not take the same amount of time when moving between characters. 2. It disrupts the main thread which I think is the cause of the camera view freezing. However, I think it is also possible that activating the flashlight while the camera is on freezes the signal, causing the glitch.

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)
        }
    }
}

回答1:


Re your first concern, the use of a series of asyncAfter are going to suffer from “timer coalescing”, where the OS will group future, individually scheduled timers to fire all at once, to make best use of the device battery (the less often the OS needs to wake up the device, the better the battery life). The further out the scheduled timers are, the more coalescing the OS will do.

One can avoid this by using a repeating 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() }
    }
}



回答2:


Something as simple as this may also work:

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")
}



回答3:


I have fixed the issue now. The first problem was solved with the timer code provided by Rob. This stopped the freezing of the main thread and now the code iterates through the String with consistent timing between. The second problem was fixed by eliminating multiple instances of AVCaptureDevice, once that was fixed the code worked. Thanks for the help!




回答4:


I think camera freezes because you call its property every time in for loop, do it once, pass its property to obtain in your func

try this code to fix your glitch

    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)
    }
}


来源:https://stackoverflow.com/questions/57213255/how-do-i-delay-a-for-loop-in-swift-without-interrupting-the-main-thread

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!