Swift中的高效且一致的时序(GCD)

时间:2018-06-09 14:25:42

标签: swift timer background

我正在努力工作的健身应用程序要求运动数据每0.1秒附加到一个阵列,然而,当应用程序背景或设备被锁定时发生的节能过程意味着这种精度漂移到最多一秒钟。

我知道传统的计时器通常遵循概述的行为,因此一直在研究替代方法,特别是GCD计时器。 A tutorial written on Medium解释了一种这样的方法,但是,我无法成功执行这些功能。

我正在跑步:

let t = RepeatingTimer(timeInterval: 0.1)
    t.eventHandler = {
        print("Timer Fired")
    }
    t.resume()

在我的viewDidLoad函数中,但没有用 - 没有打印文本。

override func viewDidLoad() {}

我的完整代码如下:

import Foundation
import UIKit
import SystemConfiguration

class accelVController: UIViewController {
    override func viewDidLoad() {
       let t = RepeatingTimer(timeInterval: 0.1)
       t.eventHandler = {
          print("Timer Fired")
       }
       t.resume()
   }
}



/// RepeatingTimer mimics the API of DispatchSourceTimer but in a way that prevents
/// crashes that occur from calling resume multiple times on a timer that is
/// already resumed (noted by https://github.com/SiftScience/sift-ios/issues/52
class RepeatingTimer {

    let timeInterval: TimeInterval

    init(timeInterval: TimeInterval) {
        self.timeInterval = timeInterval
    }

    private lazy var timer: DispatchSourceTimer = {
        let t = DispatchSource.makeTimerSource()
        t.schedule(deadline: .now() + self.timeInterval, repeating: self.timeInterval)
        t.setEventHandler(handler: { [weak self] in
            self?.eventHandler?()
        })
        return t
    }()

    var eventHandler: (() -> Void)?

    private enum State {
        case suspended
        case resumed
    }

    private var state: State = .suspended

    deinit {
        timer.setEventHandler {}
        timer.cancel()
        /*
         If the timer is suspended, calling cancel without resuming
         triggers a crash. This is documented here https://forums.developer.apple.com/thread/15902
         */
        resume()
        eventHandler = nil
    }

    func resume() {
        if state == .resumed {
            return
        }
        state = .resumed
        timer.resume()
    }

    func suspend() {
        if state == .suspended {
            return
        }
        state = .suspended
        timer.suspend()
    }

} 

如果有人可以在后台一致地提供改进的代码执行方法的建议,或者可以帮助我实现包含的代码,我将非常感激。

更新 该应用程序主要基于跌倒检测,但具有与之关联的位置元素。

它基于三个阵列。

数组a :包含时间戳,加速度是否低于最小阈值(1),是否大于最大阈值(2),或者都不是(0)。它本质上是一个寄存器,2D - [[timestamp,status]]

数组b :是循环遍历数组a 并查找状态值为1的结果。如果找到,则会追加元素的时间戳(仅限)到阵列(从 a b )。

数组c :当检查2秒的时间段是否包含1,然后是2(来自数组b )的函数时,会附加值。这被视为潜在的下降。时间戳仅附加。

然后循环播放数组c并在3秒的过程中查看掉落后的不活动时段,这表明失去意识并需要发送帮助。

我很欣赏这种方法可能效率低下,而不是最好的方法,但是,对Swift知之甚少,这对我来说是最明显的,并且如果不考虑背景,则有效。我非常乐意接受有关更合适方法的建议。注意跌倒的行动和方法是based off of this paper.

我的代码可以是seen here。为调试打印数量道歉。

1 个答案:

答案 0 :(得分:2)

这是错误的做法。 Core Motion为运动处理器提供了一个高效的界面,而您的应用程序无需经常启动定时器。定时器不会在后台获得荣誉,因此这种方法在任何情况下都不会起作用。您在短时间的背景活动中看到了一些偏差,但如果再多花几分钟,您通常会发现您的应用完全停止生成定时器事件。

如果您想要的是加速度计事件,请将game_info = [game_name, game_publisher, game_console, game_genre, game_length, game_developer] with open("game.csv", 'w') as outfile: csv.register_dialect('custom', delimiter='\n', quoting=csv.QUOTE_NONE, escapechar='\\') writer = csv.writer(outfile,'custom') row = game_info writer.writerow(row) 设置为您想要的值(0.1)并调用CMMotionManager.accelerometerUpdateInterval来处理事件。别忘了启用"背景模式:位置更新"在您的应用功能中。

也就是说,您通常不应该尝试在Core Motion层管理健身应用。 Apple通过HealthKit startAccelerometerUpdates(to:withHandler)提供更强大和更高级别的支持。不可否认,HealthKit是一个令人难以置信的令人讨厌的API,但它直接绑定到iOS已用于跟踪锻炼的系统,因此您可以免费获得许多功能(特别是帮助解决许多复杂的过滤问题)。 p>