HaishinKit.swift/Sources/Util/TimerDriver.swift

76 lines
1.9 KiB
Swift

import Foundation
public protocol TimerDriverDelegate: AnyObject {
func tick(_ driver: TimerDriver)
}
// MARK: -
public class TimerDriver {
public var interval: UInt64 = MachUtil.nanosToAbs(10 * MachUtil.nanosPerMsec)
var queue: DispatchQueue?
weak var delegate: TimerDriverDelegate?
private var runloop: RunLoop?
private var nextFire: UInt64 = 0
private var timer: Timer? {
didSet {
oldValue?.invalidate()
timer.map {
RunLoop.current.add($0, forMode: RunLoop.Mode.common)
}
}
}
public func setDelegate(_ delegate: TimerDriverDelegate, withQueue: DispatchQueue? = nil) {
self.delegate = delegate
self.queue = withQueue
}
@objc
private func on(timer: Timer) {
guard nextFire <= mach_absolute_time() else {
return
}
if let queue: DispatchQueue = queue {
queue.sync {
self.delegate?.tick(self)
}
} else {
delegate?.tick(self)
}
nextFire += interval
}
}
extension TimerDriver: Running {
// MARK: Running
public var isRunning: Atomic<Bool> {
.init(runloop != nil)
}
public func startRunning() {
DispatchQueue.global(qos: .userInteractive).async {
guard self.runloop == nil else {
return
}
self.timer = Timer(
timeInterval: 0.0001, target: self, selector: #selector(self.on), userInfo: nil, repeats: true
)
self.nextFire = mach_absolute_time() + self.interval
self.delegate?.tick(self)
self.runloop = .current
self.runloop?.run()
}
}
public func stopRunning() {
guard let runloop: RunLoop = runloop else {
return
}
timer = nil
CFRunLoopStop(runloop.getCFRunLoop())
self.runloop = nil
}
}