在 iOS 开发中,凡是用到系统时间的,都要考虑一个问题:对时。有些业务是无需对时,或能够以用户时间为准的,好比动画用到的时间、一些日程类应用等。但电商相关的业务大都不能直接使用设备上的时间,而是须要跟服务器校准后的时间,例如:javascript
能够看出,对时这个需求是很是广泛的。不过实现起来并不难,在这里分享一下咱们的经验。java
之因此叫解决方案,是由于这个功能不单是 app 端加几行代码,而是先后端配合完成的。大概思路以下:后端
服务器的时间戳能够加在 response body 里做为公共字段。在个人项目里,由于有少许 get 请求,因此放在了 response header 里。代码相似以下:缓存
+ (void)handleSuccessResponse:(id)responseObject operation:(AFHTTPRequestOperation *)operation responseType:(Class)responseClass success:(void (^)(id))successBlock failure:(void (^)(NSError *))failureBlock {
long long timestamp = [[operation.response.allHeaderFields objectForKey:@"Response-Timestamp"] longLongValue];
[HAMDateTimeUtils updateServerTime:timestamp];
}复制代码
每次网络请求成功时更新时间差的缓存。服务器
一个小的注意点是,处理 timestamp 最好始终用 long long 类型。由于 timestamp 传统上是以毫秒为单位的(虽然在 iOS 这个奇葩系统里 NSTimeInteval 是以秒为单位),在 32 位系统上 long 和 NSInteger 都存不下,会溢出。固然,如今 32 位系统的设备已经不常见了。网络
在更新缓存时,把服务器时间与本地当前的时间差保存在单例里。app
- (void)updateServerTime:(long long)timestamp {
NSTimeInterval timeInteval = timestamp / 1000.0 - [[NSDate date] timeIntervalSince1970];
[self sharedInstance].timeIntevalDifference = timeInteval;
}复制代码
须要使用时间时,根据当前时间和缓存过的时间差,计算校准后的时间:框架
+ (NSDate*)currentTime {
NSDate* serverDate = [NSDate dateWithTimeIntervalSinceNow:[self sharedInstance].timeIntevalDifference];
return serverDate;
}
// 以毫秒为单位
+ (long long)currentTimeStamp {
NSTimeInterval localTime = [[NSDate date] timeIntervalSince1970];
NSTimeInterval timeDifference = [WNYDateTimeUtils sharedInstance].timeIntevalDifference;
return (long long)((localTimeStamp + timeDifference) * 1000);
}复制代码
使用时只需调用 [HAMDateTimeUtils currentTime]
或 [HAMDateTimeUtils currentTimeStamp]
便可。测试
Q:这样得出的时间准确吗?
A:会有必定偏差。缘由在于,服务器返回的时间戳是从服务器开始返回数据的时间,到客户端接收时会有一点延迟。不过对于咱们的后台,这个延迟通常 <100 ms,对于咱们的业务来讲没什么影响。
若是对准确性要求更高,能够考虑使用专门的对时接口,不知道国家天文台有没有……
另外,这种对时的方案只是用于优化 UI 层面的显示,不能防止用户恶意的篡改。要始终记住客户端的时间戳是不可信的,后端业务凡是使用时间都务必用服务器的时间。优化
Q:缓存的时候,为何只存在单例里,不持久化存储? A:这个我也考虑过,主要是以为再次启动的时候,时间差可能会发生变化,感受持久化没有太大的必要。若是以为有必要的话,也能够在 userDefault 里存一份,启动时取出来便可。