当系统时钟改变时,是否仍要运行代码?

例如:当时钟增加一分钟时,运行print(systemtime),我将当前时间存储在其中的变量。

这只是出于我自己的测试目的,现在我有一个计时器,每分钟重复一次,获取当前时间并打印出来。

但是,我正在寻找一种在iOS中将时间准确更改到控制台的方式print

从更一般的意义上讲,这是一种在iOS时钟更改时触发运行代码的方法。

最佳答案

一言以蔽之。 iOS上的实时时钟具有亚微秒级的精度。它使用一个double值来报告自iOS“epoch date”以来的时间(以分数秒为单位)。
以某个任意间隔(每秒60秒)调用一个函数不是系统函数。
也就是说,您可以设置一个与屏幕刷新同步的CADisplayLink(低开销计时器),并且当时间在每分钟更改的某个阈值之内时(例如fmod(NSdate.timeIntervalSinceReferenceDate, 60) < 0.02),您可以将消息记录到控制台。 d需要使用阈值,因为您设置的任何间隔都可能不会精确地出现在所需的标记上。
编辑
正如Matt在下面的注释中指出的那样,CADisplayLink可能会导致设备CPU“热运行”并耗尽电池的速度比预期的更快。
如果您不需要毫秒级的精度,那么可以做一些数学运算来计算下一分钟的平均值,并在适当的延迟后启动一个重复计时器可能是一个不错的选择。像这样:

var timer: Timer?

func startMinuteTimer() {
  let now = Date.timeIntervalSinceReferenceDate
  let delayFraction = trunc(now) - now

  //Caluclate a delay until the next even minute
  let delay = 60.0 - Double(Int(now) % 60) + delayFraction

  DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
    self.timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) {
      timer in
      //Put your code that runs every minute here.
    }
  }
}
NSTimer(在Swift 3中为Timer)的分辨率大约为1/50秒,因此代码应将更新时间缩短到目标时间的0.02秒以内。
另外,只要您的应用程序变为非 Activity 状态然后再次处于 Activity 状态,您就必须终止上述计时器并重新启动它,因为它将在您的应用程序不运行时暂停,并且即使在几分钟后也不会同步启动。
编辑#2:
正如我对答案的评论所指出的那样,上面的代码在开始执行后的第一分钟就无法启动。假设您要在每分钟运行的类中有一个timeFunction函数,则可以这样解决:
  DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
    self.timeFunction()  //Call your code that runs every minute the first time
    self.timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) {
      timer in
        self.timeFunction() //Call your code that runs every minute.
    }
  }
编辑#3:
我在示例iOS应用程序中使用了上面的代码,发现如果您暂停应用程序(将其交换到后台),则计时器将按预期停止触发。当您恢复应用程序时,计时器几乎立即触发,与“按时”时间不同步。然后在下一个分钟时间再次开始。
为了避免在接到恢复 call 时的下班时间 call ,您必须侦听暂停和恢复事件,使暂停事件的计时器无效,并在恢复事件时重新创建计时器。 (如果没有将您的应用设置为在后台运行,则无法使其在后台或手机被锁定时保持每分钟触发一次。)
编辑#4:
这是一个正在运行的“单视图”应用程序中的视图控制器代码,它完成了所描述的所有工作:添加willResignActiveNotification和didBecomeActiveNotification的观察者,启动单次计时器以与分钟的变化同步,然后每分钟重复一次计时器。它具有代码,可在每次触发计时器时在标签上显示时间,并在每次更新时将时间附加到文本视图中,以便您可以查看行为并记录暂停/继续事件。
它有2个网点:
  • timeLabel(显示当前时间的UILabel)
  • timeTextView:一个UITextView,它显示时间日志和挂起/恢复事件。
  • import UIKit
    
    class ViewController: UIViewController {
    
        @IBOutlet weak var timeLabel: UILabel!
        @IBOutlet weak var timeTextView: UITextView!
        weak var timer: Timer?
        var observer1, observer2: Any?
        lazy var timeFormatter: DateFormatter = {
            let formatter = DateFormatter()
            formatter.dateFormat = "HH:mm.ss.SSS"
            return formatter
        }()
    
        func appendStringToLog(string: String) {
            let newTimeLogText = timeTextView.text + "\n" + string
            timeTextView.text = newTimeLogText
        }
    
        func timeFunction() {
            let timeString = timeFormatter.string(from: Date())
            timeLabel.text = timeString
            appendStringToLog(string: timeString)
            print(timeString)
        }
        func startMinuteTimer() {
            let now = Date.timeIntervalSinceReferenceDate
            let delayFraction = trunc(now) - now
    
            //Caluclate a delay until the next even minute
            let delay = 60.0 - Double(Int(now) % 60) + delayFraction
    
            //First use a one-shot timer to wait until we are on an even minute interval
            self.timer = Timer.scheduledTimer(withTimeInterval: delay, repeats: false) { timer in
    
                //The first time through, call the time function
                self.timeFunction()  //Call your code that runs every minute the first time
    
                //Now create a repeating timer that fires once a minute.
                self.timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) {
                    timer in
                    self.timeFunction() //Call your code that runs every minute.
                }
            }
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            //Observe the willResignActiveNotification so we can kill our timer
            observer1 = NotificationCenter.default.addObserver(forName: UIApplication.willResignActiveNotification, object: nil, queue: nil) { notification in
                let string = "Suspending. Stopping timer at \(self.timeFormatter.string(from: Date()))"
                print(string)
                self.appendStringToLog(string: string)
                self.timer?.invalidate()
            }
    
            //Observe the didBecomeActiveNotification so we can start our timer (gets called on app launch and on resume)
            observer2 = NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: nil) { notification in
                let string = "Becoming active. Starting timer at \(self.timeFormatter.string(from: Date()))"
                print(string)
                self.appendStringToLog(string: string)
                self.startMinuteTimer()
            }
        }
    }
    

    10-05 20:48
    查看更多