IOS 开发APP之关于时间处理详细介绍

2020-01-18 19:11:16王振洲

iOS系统还记录了上次设备重启的时间。可以通过如下API调用获取:


#include <sys/sysctl.h>
- (long)bootTime
{
#define MIB_SIZE 2
  int mib[MIB_SIZE];
  size_t size;  
  struct timeval boottime;

  mib[0] = CTL_KERN;
  mib[1] = KERN_BOOTTIME;
  size = sizeof(boottime);  
  if (sysctl(mib, MIB_SIZE, &boottime, &size, NULL, 0) != -1)
  {    
    return boottime.tv_sec;
  }  
  return 0;
}

返回的值是上次设备重启的Unix time。

这个API返回的值也会受系统时间影响,用户如果修改时间,值也会随着变化。

有了以上获取时间的各种手段,我们再来看看一些场景之下的具体应用。

场景一,时间测量

我们做性能优化的时候,经常需要对某个方法执行的时间做记录,就必然会用到上面提到的一些获取时间的方法。

在做方法执行时间的benchmark的时候,我们获取时间的方法要满足两个要求,一是精读要高,而是API本身几乎不耗CPU时间。

客户端做性能优化一般是为了主线程的流畅性,而我们知道UI线程如果遇到超过16.7ms的阻塞,就会出现掉帧现象,所以我们关注的时间的精读实际上是在毫秒(ms)级别。我们写客户端代码的时候,基本上都是处于ms这一维度,如果一个方法损耗是0.1ms,我们可以认为这个方法对于流畅性来说是安全的,如果经常看到超过1ms或者几个ms的方法,主线程出现卡顿的几率就会变高。

上面几种获取时间的方式精读上都是足够的,比如一个NSDateAPI调用返回的精读是0.000004 S,也就是4微秒,CACurrentMediaTime()返回的精读也在微秒级别,精读上都符合要求。不过有一种看法,认为NSDate属于类的封装,OOP高级语言本身所带来的损耗可能会影响最后的实际结果,在做benchmark的时候不如C函数调用精准,为了验证这一说法,我写了一段简单的测试代码:


int testCount = 10000;
double avgCost = 0;
for (int i = 0; i < testCount; i ++) {  
  NSDate* begin = [NSDate date];  
  NSLog(@"a meaningless log");
  avgCost += -[begin timeIntervalSinceNow];
}
NSLog(@"benchmark with NSDate: %f", avgCost/testCount);

avgCost = 0;
for (int i = 0; i < testCount; i ++) {  
  double startTime = CACurrentMediaTime();  
  NSLog(@"a meaningless log");  
  double endTime = CACurrentMediaTime();
  avgCost += (endTime - startTime);
}
NSLog(@"benchmark with CACurrentMediaTime: %f", avgCost/testCount);

输出结果为:


benchmark with NSDate: 0.000046
benchmark with CACurrentMediaTime: 0.000037

可以看出CACurrentMediaTime与NSDate代码本身的损耗差异在几微秒,而我们做UI性能优化的维度在毫秒级别,几个微秒的差异完全不会影响我们最后的判断结果。所以使用NSDate做benchmark完全是可行的,以下是我常用的两个宏: