【问题标题】:How do I delay a for loop in swift without interrupting the main thread?如何在不中断主线程的情况下快速延迟 for 循环?
【发布时间】:2019-07-26 04:32:25
【问题描述】:

我正在尝试以延迟 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)
        }
    }
}

【问题讨论】:

    标签: swift iphone flashlight


    【解决方案1】:

    您首先关心的是,使用一系列asyncAfter 将遭受“计时器合并”的影响,操作系统将在其中组合未来的、单独安排的计时器以一次全部触发,以充分利用设备电池(操作系统需要唤醒设备的频率越低,电池寿命越长)。计划的计时器越远,操作系统的合并就越多。

    可以通过使用重复的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() }
        }
    }
    

    【讨论】:

    • 非常感谢,这解决了时间间隔的问题。我仍然遇到一个问题,即当手电筒打开时,相机馈送停止工作。您知道是否可以在启用相机馈送的同时操作手电筒?有趣的是,手电筒无法停止我朋友手机(iPhone 7)上的摄像头,但它不适用于我的手机(iPhone X)。再次感谢您的帮助!
    • @I.Winson - “您知道是否可以在启用摄像头馈送的同时操作手电筒?” ...您是否在代码的其他地方执行任何lockForConfigurationisInUseByAnotherApplicationisSuspended 返回什么值?
    【解决方案2】:

    像这样简单的事情也可能有效:

    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】:

      我现在已经解决了这个问题。第一个问题是用 Rob 提供的计时器代码解决的。这停止了​​主线程的冻结,现在代码以一致的时间迭代字符串。 第二个问题是通过消除AVCaptureDevice 的多个实例来解决的,一旦解决了代码就可以工作。 感谢您的帮助!

      【讨论】:

        【解决方案4】:

        我认为相机冻结是因为你每次在 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)
            }
        }
        

        【讨论】:

        • 感谢您的回复,我对您的解决方案进行了一些修改,但仍然存在故障问题。 flash 方法有一些我需要的逻辑,所以我保留了它,我不完全确定 isOn 变量的重要性是什么。有谁知道是否可以在不引起故障的情况下控制手电筒?仅当手电筒打开时才会发生冻结。
        • func scriptReader(){ let str = "10101000001111110000110" guard let device = AVCaptureDevice.default(for: .video) else { return } guard device.hasTorch else { return } for i in 0..&lt;str.count { DispatchQueue.main.asyncAfter(deadline: .now() + Double(i) * 0.5) { let index = str.index(str.startIndex, offsetBy: i) self.flash(device: device, number: str[index]) } } }
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-03
        • 1970-01-01
        • 1970-01-01
        • 2020-11-15
        • 2021-11-09
        • 2020-02-29
        相关资源
        最近更新 更多