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完全是可行的,以下是我常用的两个宏:










