2016-04-20 120 views
1

我有下面的代码根据存储在NSDefault变量checkOutTime中的值初始化NSTimer。NSTimer在触发之前触发

假设如果我将此变量中的时间更改为过去的时间(当前时间之前),并重新初始化定时器,它会自动触发并调用回调方法ForceCheckOut()

我需要定时器才会在给定的启用日期启动。

func InitilizeAutoCheckOut(isExtendedCheckout:Bool) 
{ 

     let checkOutTime = self.defaults.valueForKey("checkOutTime") as! String 

     print(checkOutTime) 



     if !checkOutTime.isEmpty 
     { 
      let date = self.StringToDate(checkOutTime, IsExtendedCheckout: isExtendedCheckout) 

      print(date) 

      let autoCheckOutTimer = NSTimer(fireDate: date, interval: 24*60*60, target: self, selector: #selector(DashboardViewController.ForceCheckOut), userInfo: nil, repeats: true) 
      NSRunLoop.mainRunLoop().addTimer(autoCheckOutTimer, forMode: NSDefaultRunLoopMode) 

     } 

} 
+0

检查时区为系统时区与否。 –

回答

2

正如在几个地方(here is a good example)解释说,NSTimerfireDate实际上不是一个日历日期时间在该定时器触发。把它想象成一个要求倒计时的机制更准确。值得注意的是,NSTimer不考虑时钟重置,背景等。它基本上计算一个数字直到它应该触发,周期性地减少该数字,并且一旦该数字触发该数字< = 0。改变本地时钟不影响该数字,并且暂停该过程(例如,通过后台应用程序)将导致倒计时从其当前值恢复,而不考虑它睡着的时间。这可以说是不直观的,因为fireDate强烈暗示着“在这个日历日期发生火灾”的合约,但API是它的原型。

有了这些知识,你可以凭直觉,你所看到的行为是正常的:如果设置在过去的日期,你基本上要求计时器从(负数)倒数,所以计时器立即认识到它的火情已经满足(剩余时间< = 0),一旦它被runloop唤醒就会触发。

但即使没有这方面的知识,我也会认为这种行为满足了更多的期望。考虑:

无论[tolerance]的值如何,系统保留对某些定时器应用少量容差的权利。 - NSTimer class ref

如果您将来在非常短的时间内计划一个计时器,计时器可能不会被runloop检查,直到其计划的时间已经过去。即使时间过去了,计时器也会启动,计时器会在特定的时间或多或少发生火灾,从而满足程序员的期望。

此外,您期望的行为会引发围绕管理定时器的一整套复杂问题。如果您在将来创建带有启动日期的计时器,但在启动日期过后才添加它?计时器是否应该启动?目前的行为消除了这个决定。

你最简单的解决方案可能是检查消防和你约会安排在计时器前,只有将它添加到运行循环,如果火势日期符合您的条件:火日期

let date = self.StringToDate(checkOutTime, IsExtendedCheckout: isExtendedCheckout) 
let comparison = date.compare(NSDate()) 
guard comparison != .OrderedAscending else { 
    print("date \(date) has already passed. Not scheduling timer.") 
    return 
}