【问题标题】:How to schedule iOS method call at exact time如何在准确的时间安排 iOS 方法调用
【发布时间】:2014-04-04 14:51:27
【问题描述】:

我希望 iOS 在未来的准确时间调用我的对象的方法。从当前时间开始,调用该方法的时间最长可能为 4 小时。

我知道我可以使用NSTimer,但我担心它对于我的应用程序来说不够精确(即在 30-50 毫秒内):“计时器不是实时机制;它会触发仅当添加了计时器的运行循环模式之一正在运行并且能够检查计时器的触发时间是否已过时。"

我还看到有人提到使用CADisplayLink 来提高精度,但更多的是在interval timing 的上下文中。

另一个替代方案可能是threading,以避免由系统运行循环引起的延迟。

对于我想做的事情,哪个被认为是“最佳”选择,或者还有什么我应该看的?或者如果我的应用程序中真的很少发生其他事情,NSTimer 是否足够精确?谢谢!

【问题讨论】:

    标签: ios timer


    【解决方案1】:

    NSTimer 的一个更好的替代方案是 GCD,您可以使用 dispatch_after 在一段时间后调用方法,它甚至更好。

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // TO DO METHOD CALL.
    });
    

    【讨论】:

    • 在上面的代码中,该方法将在 10.0 秒后执行。
    • 谢谢。非常直截了当。我最终可能会使用 Buunk 下面提供的一些类似答案,但首先需要进行更多测试。
    【解决方案2】:

    NSTimer 绝对不是答案。如果您想要任何类型的准确性,我强烈建议您完全避免使用NSTimer

    NSDate 并将返回一个 NSTimeInterval,它是具有亚毫秒精度的双精度。 NSTimeInterval 以秒为单位,但它使用双精度来为您提供更高的精度。

    砧板上的下一个是CoreAnimation,其功能为CFAbsoluteTimeGetCurrent。许多人认为这比以前的选项更精确。它不是更准确,或更精确。同样令人困惑的是,这个时间函数返回系统正常运行时间的值。由于您试图准确地安排某些事情,因此对您来说毫无用处。

    如果您确实需要更高的精度,唯一的答案是mach_absolute_time。花点时间了解这门课。这将以处理器相关的滴答声将时间返回给您。虽然它需要一些按摩才能回到墙上时间,因为它实际上是在跟踪 CPU 周期,你不能比这更准确。

    查看Apple's Tech Note on Precise Timing and Measurements

    编辑:您似乎想在未来的某个特定时间运行一些代码,我想到的第一个也是最简单的解决方案是 UILocalNotifications。他们毫不费力地处理时区和跟踪,并且由于fireDateNSDate,您应该看到与另一个NSDate 相同的精度,并且NSTimer 更准确

    【讨论】:

    • 谢谢。我实际上是在尝试根据外部 SNTP 服务器在特定时间运行代码。为此,我正在计算要应用于未来时间的“调整”。 UILocalNotification 会在这段时间的 50 毫秒内调用我的方法,还是(或者)我应该提前 5 秒安排它,然后使用 Grand Central Dispatch 来获得更精确的结果? (代码需要在屏幕上绘制,如果这有什么不同的话。)
    • 刚刚回到这个... UILocalNotification 不准确。见stackoverflow.com/questions/5064460/…。我去看看 GCD 怎么样。
    • 我将其更改为正确答案。 dispatch_after 在我的测试后期往往会显着触发(0.5+ 秒)。所以我最终创建了一个新线程,然后在其中调用 mach_wait_until() 并计算延迟,然后再调用我的对象的方法。非常精确。
    • @ScottyB,你能解释一下你是怎么做到的吗?您的问题似乎是唯一询问确切时间的 SO 问题,但在此评论中您谈论的是计算延迟——这听起来像所有其他关于相对时间的问题。如果您需要在下午 5 点准确地触发某些东西,您如何让“直到”输入 mach_wait_until()?
    • @LeMotJuiced 如果您发布其他问题,我很乐意回答!
    【解决方案3】:
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
            dispatch_async(queue, ^{
                double delayInSeconds = 0.2; 
                dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); 
                dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ [self doTimer];
            });
    });
    
    - (void) doTimer {
      // your code 
    }
    

    【讨论】:

      【解决方案4】:

      我在上一个项目中遇到了同样的问题,这就是我解决它的方法。首先,您需要从开始计算时间开始保存时间。就我而言,我选择使用NSDefault

      [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:@"ReminderStart"]
      

      然后当你需要决定是否调用你的方法时,你从NSDefault获取提醒开始时间,并用它与当前时间进行比较:

      NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
          NSDateComponents *components = [gregorianCalendar components:NSMinuteCalendarUnit
                                                              fromDate:REMINDER_START_DATE
                                                                toDate:[NSDate date]
                                                               options:0];
      if (components.minute > 30){
         //call your method here if the time difference is larger than 30 minutes
      }
      

      在我的代码中,我检查了以分钟为单位的时差,但如果您愿意,您可以完全将其调整为秒、小时、天或周。希望这会有所帮助。

      【讨论】:

      • 我不确定你是否明白我在问什么。我正在寻找一种在 30-50 毫秒内获得精确警报的方法。
      • 我猜你需要的是调用那个方法,即使你的应用程序没有激活?如果是这样,请更新您的问题,说实话,这有点误导。
      • 不,如果应用程序未处于活动状态/在后台,则不需要调用该方法。用户将错过!
      • 如果是这样,我不确定您是否理解我在此处发布的内容。这个想法很简单:一旦你有了闹钟的开始时间,你只需要检查时间差来决定是否是时候调用你的方法了。您可以将该检查放入任何需要它的视图控制器的viewWillAppear/或任何其他适合您需要的位置。您问题中的选项都比必要的复杂。如果这仍然不是您想要的,请进一步解释,以便我提供帮助。
      • 由于 NSDateComponents 的最细粒度是秒(NSSecondCalendarUnit),它是否能够在所需时间为 50 毫秒的情况下调用方法?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-27
      相关资源
      最近更新 更多