1.什么是內部時鍾
在我們做iOS開發的過程中,我們經常要與時間打交道,[NSDate date]是我們常用的取時間的一種方式,但是[NSDate date] 這種方式只能取系統的當前時間。也就是說:當前我們手機的時間是什么時間,取出來的值,就是多少。
如果用戶把系統的時間改了呢?那么[NSDate date]取出來的值,還是我們想要的嗎???在一些應用的開發中,我們在沒有網絡的狀態下,不能取網絡時間,依靠系統時間,是可以篡改的。所以這個時候,我們要自己要在程序的內部定制一個自己的內部時鍾。
2.實現內部時鍾的思路
1.要有一個時間作為基本的參照點(一般應用都會與服務器打交道,所以發請求給服務器,取服務器的時間是比較合適的)
2.要有一個標記點(一般取待機時長)
3.在每次進入程序的時候,或者登錄的時候,取服務器的時間存起來,然后再取當前的待機時間存起來,每次要獲取當前時間的時候,再取待機時長跟之前的存儲的待機時長比較,獲得差值。將存儲的服務器時間加上差值,就獲得想要的當前時間。
3.具體實現步驟
0.用到的宏:
//開機時間 #define SWStartTime @"startTime" //服務器時間 #define SWServerTime @"serverTime" //登錄時的待機時長 #define SWSinceNow @"sinceNow"
1.獲取待機時長
/** * 待機時間(從系統啟動的那一刻開始獲取的時間間隔) */ + (time_t)uptime { struct timeval boottime; int mib[2] = {CTL_KERN, KERN_BOOTTIME}; size_t size = sizeof(boottime); time_t now; time_t uptime = -1; (void)time(&now); if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0) { uptime = now - boottime.tv_sec; } return uptime; }
2.存儲服務器時間及待機時長
/** * 存儲服務器時間及待機時長 * * @param serverTime 服務器時間 */ + (void)firstTimeWithLogin:(NSString *)serverTime { NSTimeInterval timer = (NSTimeInterval)[self uptime]; NSString *sinceNow = [NSString stringWithFormat:@"%f",timer]; NSUserDefaults *UserDefaults = [NSUserDefaults standardUserDefaults]; //存儲登錄時獲取的服務器時間 [UserDefaults setObject:serverTime forKey:SWServerTime]; //存儲登錄時獲取的待機時長 [UserDefaults setObject:sinceNow forKey:SWSinceNow]; }
3.獲得當前的時間(以服務器時間為基准)
+ (NSDate *)dateOfNow { NSDateFormatter *formatter = [[NSDateFormatter alloc]init]; [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; NSUserDefaults *UserDefaults = [NSUserDefaults standardUserDefaults]; //取出登錄時獲取的服務器時間 NSString * serverText = [UserDefaults objectForKey:SWServerTime]; NSDate *FirstServer = [formatter dateFromString:serverText]; NSString *firstText = [UserDefaults objectForKey:SWSinceNow]; CGFloat first = firstText.floatValue; NSTimeInterval timer = (NSTimeInterval)[self uptime]; CGFloat second = (CGFloat)timer; //差值 CGFloat finaly = second - first; NSTimeInterval interval = (NSTimeInterval)finaly; //最后的時間 NSDate *finalyDate = [FirstServer dateByAddingTimeInterval:interval]; return finalyDate; }
4.深度探討
- 為什么獲取待機時間不用SystemUptime這種方法?
答案 :SystemUptime這種獲取待機時間的方式在我們設備深度睡眠的時候,獲取的值會有誤差,而上面我所用的方法不會。親測!!!
- 如果我要獲取手機的開機時間,怎么辦?
答案 :
/** * 獲得開機時間 */ + (NSString *)getUpTime{ NSString * proc_useTiem; int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, 0}; size_t miblen = 4; size_t size; //返回0,成功;返回-1,失敗 int st = sysctl(mib, miblen, NULL, &size, NULL, 0); struct kinfo_proc * process = NULL; struct kinfo_proc * newprocess = NULL; do { size += size / 10; newprocess = realloc(process, size); if (!newprocess) { if (process) { free(process); process = NULL; } return nil; } process = newprocess; st = sysctl(mib, miblen, process, &size, NULL, 0); } while (st == -1 && errno == ENOMEM); if (st == 0) { if (size % sizeof(struct kinfo_proc) == 0) { int nprocess = size / sizeof(struct kinfo_proc); if (nprocess) { for (int i = nprocess - 1; i >= 0; i--) { @autoreleasepool{ //進程的時間 double t = process->kp_proc.p_un.__p_starttime.tv_sec; double s = process->kp_proc.p_un.__p_starttime.tv_usec; double finaly = t + s *0.000001;
//將其轉為具體時間 proc_useTiem = [self timeWithBoot:finaly]; } } free(process); process = NULL; return proc_useTiem; } } } return nil; }
/** * 轉為具體時間 */ + (NSString *)timeWithBoot:(double)interval { NSDateFormatter *format = [[NSDateFormatter alloc]init]; format.timeZone = [NSTimeZone timeZoneWithName:@"shanghai"]; [format setDateStyle:NSDateFormatterMediumStyle]; [format setTimeStyle:NSDateFormatterShortStyle]; //注意先后順序 [format setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"];
NSDate *date = [NSDate dateWithTimeIntervalSince1970:interval]; NSString *bootTime = [format stringFromDate:date]; return bootTime; }
PS:如有問題請留言或關注我的新浪微博http://weibo.com/3216725234私信我!!!!