欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

IOS 開發(fā)APP之關(guān)于時間處理詳細介紹

 更新時間:2016年12月21日 10:04:14   投稿:lqh  
這篇文章主要介紹了IOS 開發(fā)APP之關(guān)于時間處理詳細介紹的相關(guān)資料,開發(fā)APP 不僅需要對API的調(diào)用還需要對時間相關(guān)的各種API之間的差別,再因場景而異去設(shè)計相應(yīng)的機制,需要的朋友可以參考下

IOS 時間處理

做App避免不了要和時間打交道,關(guān)于時間的處理,里面有不少門道,遠不是一行API調(diào)用,獲取當前系統(tǒng)時間這么簡單。我們需要了解與時間相關(guān)的各種API之間的差別,再因場景而異去設(shè)計相應(yīng)的機制。

時間的形式

在開始深入討論之前,我們需要確信一個前提:時間是線性的。即任意一個時刻,這個地球上只有一個絕對時間值存在,只不過因為時區(qū)或者文化的差異,處于同一時空的我們對同一時間的表述或者理解不同。這個看似簡單明了的道理,是我們理解各種與時間相關(guān)的復(fù)雜概念的基石。就像UTF-8和UTF-16其實都是Unicode一樣,北京的20:00和東京的21:00其實是同一個絕對的時間值。

GMT

人類對于時間的理解還很有限,但我們至少能確定一點:時間的變化是勻速的。時間前進的速度是均勻的,不會忽快忽慢,所以為了描述時間,我們也需要找到一個值,它的變化也是以均勻的速度向前變化的。

說出來你可能不信,我們?nèi)祟悶榱藢ふ疫@個參考值,來精確描述當前的時間值,都經(jīng)歷了漫長歲月的探索。你可以嘗試思考下,生活中有什么事物是隨著時間均勻變化的,它具備的數(shù)值屬性,會隨著時間處于絕對的勻速變化狀態(tài)。

前人發(fā)現(xiàn)抬頭看太陽是個好辦法,太陽總是按規(guī)律的“早起晚落”,而且“亙古不變”,可以用太陽在一天當中所處的位置來描述當前的時間。后來不同地區(qū)的文化需要交流,你這里太陽正高空照,我這可能已經(jīng)下山了,所以需要有一個公共的大家都認可的地方,以這個地方太陽的位置來做參考著,溝通起來就會方便很多。最后選擇的是英國倫敦的格林尼治天文臺所在地,以格林尼治的時間作為公共時間,也就是我們所說的GMT時間(Greenwich Mean Time)。

UTC

太陽所處的位置變化跟地球的自轉(zhuǎn)相關(guān),過去人們認為地球自轉(zhuǎn)的速率是恒定的,但在1960年這一認知被推翻了,人們發(fā)現(xiàn)地球自轉(zhuǎn)的速率正變得越來越慢,而時間前進的速率還是恒定的,所以UTC不再被認為可以用來精準的描述時間了。

我們需要繼續(xù)尋找一個勻速前進的值。抬頭看天是我們從宏觀方向去尋找答案,科技的發(fā)展讓我們在微觀方面取得了更深的認識,于是有聰明人根據(jù)微觀粒子原子的物理屬性,建立了原子鐘,以這種原子鐘來衡量時間的變化,原子鐘50億年才會誤差1秒,這種精讀已經(jīng)遠勝于GMT了。這個原子鐘所反映的時間,也就是我們現(xiàn)在所使用的UTC(Coordinated Universal Time )標準時間。

接下來我們看下iOS里,五花八門的記錄時間的方式。

NSDate

NSDate是我們平時使用較多的一個類,先看下它的定義:

NSDate objects encapsulate a single point in time,
 independent of any particular calendrical system or time zone.
 Date objects are immutable, representing an invariant time interval
 relative to an absolute reference date (00:00:00 UTC on 1 January 2001).

NSDate對象描述的是時間線上的一個絕對的值,和時區(qū)和文化無關(guān),它參考的值是:以UTC為標準的,2001年一月一日00:00:00這一刻的時間絕對值。

這里有個概念很重要,我們用編程語言描述時間的時候,都是以一個時間線上的絕對值為參考點,參考點再加上偏移量(以秒或者毫秒,微秒,納秒為單位)來描述另外的時間點。

理解了這一點,再看NSDate的一些API調(diào)用就非常清楚了,比如:

NSDate* date = [NSDate date];
NSLog(@"current date interval: %f", [date timeIntervalSinceReferenceDate]);

timeIntervalSinceReferenceDate返回的是距離參考時間的偏移量,這個偏移量的值為502945767秒,502945767/86400/365=15.9483056507,86400是一天所包含的秒數(shù),365大致是一年的天數(shù),15.94當然就是年數(shù)了,算出來剛好是此刻距離2001年的差值。

又比如,此刻我寫文章的時候,當前時間為北京時間上午11:29,看看下面代碼的輸出:

NSDate* date = [NSDate date];
NSLog(@"current date: %@", date);

current date: 2016-12-09 03:29:09 +0000??梢奛SDate輸出的是絕對的UTC時間,而北京時間的時區(qū)為UTC+8,上面的輸出+8個小時,剛好就是我當前的時間了。

NSDate和市區(qū)和文化無關(guān),所以要展示具體格式的時間,我們需要NSDateFormatter和NSTimeZone的輔助。

另外關(guān)于NSDate最重要的一點是:NSDate是受手機系統(tǒng)時間控制的。也就是說,當你修改了手機上的時間顯示,NSDate獲取當前時間的輸出也會隨之改變。在我們做App的時候,明白這一點,就知道NSDate并不可靠,因為用戶可能會修改它的值。

CFAbsoluteTimeGetCurrent()

官方定義如下:

Absolute time is measured in seconds relative to the absolute reference date of Jan 1 2001 00:00:00 GMT.
 A positive value represents a date after the reference date, a negative value represents a date before it. 
For example, the absolute time -32940326 is equivalent to December 16th, 1999 at 17:54:34.
 Repeated calls to this function do not guarantee monotonically increasing results.
 The system time may decrease due to synchronization with external time references or due to an explicit
 user change of the clock.

從上面的描述不難看出CFAbsoluteTimeGetCurrent()的概念和NSDate非常相似,只不過參考點是:以GMT為標準的,2001年一月一日00:00:00這一刻的時間絕對值。

同樣CFAbsoluteTimeGetCurrent()也會跟著當前設(shè)備的系統(tǒng)時間一起變化,也可能會被用戶修改。

gettimeofday

這個API也能返回一個描述當前時間的值,代碼如下:

struct timeval now;
struct timezone tz;
gettimeofday(&now, &tz);
NSLog(@"gettimeofday: %ld", now.tv_sec);

使用gettimeofday獲得的值是Unix time。Unix time又是什么呢?

Unix time是以UTC 1970年1月1號 00:00:00為基準時間,當前時間距離基準點偏移的秒數(shù)。上述API返回的值是1481266031,表示當前時間距離UTC 1970年1月1號 00:00:00一共過了1481266031秒。

Unix time也是平時我們使用較多的一個時間標準,在Mac的終端可以通過以下命令轉(zhuǎn)換成可閱讀的時間:

date -r 1481266031

實際上NSDate也有一個API能返回Unix time:

NSDate* date = [NSDate date];
NSLog(@"timeIntervalSince1970: %f", [date timeIntervalSince1970]);

gettimeofday和NSDate,CFAbsoluteTimeGetCurrent()一樣,都是受當前設(shè)備的系統(tǒng)時間影響。只不過是參考的時間基準點不一樣而已。我們和服務(wù)器通訊的時候一般使用Unix time。

mach_absolute_time()

mach_absolute_time()可能用到的同學比較少,但這個概念非常重要。

前面提到我們需要找到一個均勻變化的屬性值來描述時間,而在我們的iPhone上剛好有一個這樣的值存在,就是CPU的時鐘周期數(shù)(ticks)。這個tick的數(shù)值可以用來描述時間,而mach_absolute_time()返回的就是CPU已經(jīng)運行的tick的數(shù)量。將這個tick數(shù)經(jīng)過一定的轉(zhuǎn)換就可以變成秒數(shù),或者納秒數(shù),這樣就和時間直接關(guān)聯(lián)了。

不過這個tick數(shù),在每次手機重啟之后,會重新開始計數(shù),而且iPhone鎖屏進入休眠之后tick也會暫停計數(shù)。

mach_absolute_time()不會受系統(tǒng)時間影響,只受設(shè)備重啟和休眠行為影響。

CACurrentMediaTime()

CACurrentMediaTime()可能接觸到的同學會多一些,先看下官方定義:

/* Returns the current CoreAnimation absolute time. This is the result of
 * calling mach_absolute_time () and converting the units to seconds. */

CFTimeInterval CACurrentMediaTime (void)

CACurrentMediaTime()就是將上面mach_absolute_time()的CPU tick數(shù)轉(zhuǎn)化成秒數(shù)的結(jié)果。以下代碼:

double mediaTime = CACurrentMediaTime();
NSLog(@"CACurrentMediaTime: %f", mediaTime);

返回的就是開機后設(shè)備一共運行了(設(shè)備休眠不統(tǒng)計在內(nèi))多少秒,另一個API也能返回相同的值:

NSTimeInterval systemUptime = [[NSProcessInfo processInfo] systemUptime];
NSLog(@"systemUptime: %f", systemUptime);

CACurrentMediaTime()也不會受系統(tǒng)時間影響,只受設(shè)備重啟和休眠行為影響。

sysctl

iOS系統(tǒng)還記錄了上次設(shè)備重啟的時間。可以通過如下API調(diào)用獲?。?/p>

#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;
}

返回的值是上次設(shè)備重啟的Unix time。

這個API返回的值也會受系統(tǒng)時間影響,用戶如果修改時間,值也會隨著變化。

有了以上獲取時間的各種手段,我們再來看看一些場景之下的具體應(yīng)用。

場景一,時間測量

我們做性能優(yōu)化的時候,經(jīng)常需要對某個方法執(zhí)行的時間做記錄,就必然會用到上面提到的一些獲取時間的方法。

在做方法執(zhí)行時間的benchmark的時候,我們獲取時間的方法要滿足兩個要求,一是精讀要高,而是API本身幾乎不耗CPU時間。

客戶端做性能優(yōu)化一般是為了主線程的流暢性,而我們知道UI線程如果遇到超過16.7ms的阻塞,就會出現(xiàn)掉幀現(xiàn)象,所以我們關(guān)注的時間的精讀實際上是在毫秒(ms)級別。我們寫客戶端代碼的時候,基本上都是處于ms這一維度,如果一個方法損耗是0.1ms,我們可以認為這個方法對于流暢性來說是安全的,如果經(jīng)常看到超過1ms或者幾個ms的方法,主線程出現(xiàn)卡頓的幾率就會變高。

上面幾種獲取時間的方式精讀上都是足夠的,比如一個NSDateAPI調(diào)用返回的精讀是0.000004 S,也就是4微秒,CACurrentMediaTime()返回的精讀也在微秒級別,精讀上都符合要求。不過有一種看法,認為NSDate屬于類的封裝,OOP高級語言本身所帶來的損耗可能會影響最后的實際結(jié)果,在做benchmark的時候不如C函數(shù)調(diào)用精準,為了驗證這一說法,我寫了一段簡單的測試代碼:

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);

輸出結(jié)果為:

benchmark with NSDate: 0.000046
benchmark with CACurrentMediaTime: 0.000037

可以看出CACurrentMediaTime與NSDate代碼本身的損耗差異在幾微秒,而我們做UI性能優(yōu)化的維度在毫秒級別,幾個微秒的差異完全不會影響我們最后的判斷結(jié)果。所以使用NSDate做benchmark完全是可行的,以下是我常用的兩個宏:

#define TICK  NSDate *startTime = [NSDate date]
#define TOCK  NSLog(@"Time Cost: %f", -[startTime timeIntervalSinceNow])

場景二:客戶端和服務(wù)器之間的時間同步

這也是我們經(jīng)常遇到的場景,比如電商類App到零點的時候開始搶購,比如商品限購倒計時等等,這種場景下需要我們將客戶端的時間與服務(wù)器保持一致,最重要的是,要防止用戶通過斷網(wǎng)修改系統(tǒng)時間,來影響客戶端的邏輯。

比較普遍的做法是,在一些常用的Server接口里面帶上服務(wù)器時間,每調(diào)用一次接口,客戶端就和服務(wù)器時間做一次同步并記錄下來,但問題是如何防止用戶修改呢?

上面提到的NSDate,CFAbsoluteTimeGetCurrent,gettimeofday,sysctl都是跟隨系統(tǒng)時間變化的,mach_absolute_time和CACurrentMediaTime雖然是依據(jù)CPU時鐘數(shù),不受系統(tǒng)時間影響,但在休眠和重啟的時候還是會被影響??瓷先ザ疾惶m合,這里介紹下我個人的做法。

首先還是會依賴于接口和服務(wù)器時間做同步,每次同步記錄一個serverTime(Unix time),同時記錄當前客戶端的時間值lastSyncLocalTime,到之后算本地時間的時候先取curLocalTime,算出偏移量,再加上serverTime就得出時間了:

uint64_t realLocalTime = 0;
if (serverTime != 0 && lastSyncLocalTime != 0) {
  realLocalTime = serverTime + (curLocalTime - lastSyncLocalTime);
}else {
  realLocalTime = [[NSDate date] timeIntervalSince1970]*1000;
}

如果從來沒和服務(wù)器時間同步過,就只能取本地的系統(tǒng)時間了,這種情況幾乎也沒什么影響,說明客戶端還沒開始用過。

關(guān)鍵在于如果獲取本地的時間,可以用一個小技巧來獲取系統(tǒng)當前運行了多長時間,用系統(tǒng)的運行時間來記錄當前客戶端的時間:

//get system uptime since last boot
- (NSTimeInterval)uptime
{  
  struct timeval boottime;  
  int mib[2] = {CTL_KERN, KERN_BOOTTIME};
  size_t size = sizeof(boottime);  
  struct timeval now;  
  struct timezone tz;
  gettimeofday(&now, &tz);  
  double uptime = -1;  
  if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0)
  {
    uptime = now.tv_sec - boottime.tv_sec;
    uptime += (double)(now.tv_usec - boottime.tv_usec) / 1000000.0;
  }  
  return uptime;
}

gettimeofday和sysctl都會受系統(tǒng)時間影響,但他們二者做一個減法所得的值,就和系統(tǒng)時間無關(guān)了。這樣就可以避免用戶修改時間了。當然用戶如果關(guān)機,過段時間再開機,會導(dǎo)致我們獲取到的時間慢與服務(wù)器時間,真實場景中,慢于服務(wù)器時間往往影響較小,我們一般擔心的是客戶端時間快于服務(wù)器時間。

多和服務(wù)器做時間同步,再把關(guān)鍵的時間校驗邏輯放在Server端,就不會出現(xiàn)什么意外的bug了。

總結(jié)

關(guān)于時間處理的邏輯就總結(jié)到這里了,關(guān)鍵還在于我們對于時間本身的理解,對于表達時間的各種方式的理解,理解背后的原理才能選擇合適的工具。

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

相關(guān)文章

  • IOS9.0 LaunchScreen.StroyBoard自定義啟動圖片詳解

    IOS9.0 LaunchScreen.StroyBoard自定義啟動圖片詳解

    這篇文章主要介紹了IOS9.0 LaunchScreen.StroyBoard自定義啟動圖片詳解的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • Objective-C實現(xiàn)身份證驗證的方法示例

    Objective-C實現(xiàn)身份證驗證的方法示例

    這篇文章主要給大家分享了Objective-C實現(xiàn)身份證驗證的方法,文中給出了詳細的示例代碼,對大家具有一定的參考價值,需要的朋友們下面來一起看看吧。
    2017-03-03
  • 詳解iOS多線程GCD的使用

    詳解iOS多線程GCD的使用

    Grand Central Dispatch (GCD)是Apple開發(fā)的一個多核編程的解決方法,本文給大家詳細介紹IOS中GCD的使用,需要的朋友參考下
    2016-03-03
  • iOS開發(fā)中的ViewController轉(zhuǎn)場切換效果實現(xiàn)簡介

    iOS開發(fā)中的ViewController轉(zhuǎn)場切換效果實現(xiàn)簡介

    這篇文章主要介紹了iOS開發(fā)中的ViewController轉(zhuǎn)場切換效果實,主要針對iOS7以后新加入的API進行講解,需要的朋友可以參考下
    2015-09-09
  • CocoaPods1.9.0 安裝使用教程詳解

    CocoaPods1.9.0 安裝使用教程詳解

    CocoaPods是OS X和iOS下的一個第三類庫管理工具,這篇文章主要介紹了CocoaPods1.9.0 安裝使用,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-03-03
  • iOS  隱藏導(dǎo)航條和狀態(tài)欄實現(xiàn)方法

    iOS 隱藏導(dǎo)航條和狀態(tài)欄實現(xiàn)方法

    這篇文章主要介紹了 iOS隱藏導(dǎo)航條和狀態(tài)欄實現(xiàn)方法的相關(guān)資料,有時候根據(jù)需求開發(fā)APP 需要隱藏導(dǎo)航欄和狀態(tài)欄,這里提供了實現(xiàn)方法需要的朋友可以參考下
    2016-11-11
  • iOS 11 使用兩種方法替換(Method Swizzling)去掉導(dǎo)航欄返回按鈕的文字

    iOS 11 使用兩種方法替換(Method Swizzling)去掉導(dǎo)航欄返回按鈕的文字

    這篇文章主要介紹了iOS 11 使用方法替換(Method Swizzling)去掉導(dǎo)航欄返回按鈕的文字,需要的朋友可以參考下
    2018-05-05
  • iOS時間字符串格式化輸出技巧詳解

    iOS時間字符串格式化輸出技巧詳解

    本篇文章主要介紹了iOS時間格式化輸出技巧,可以將后臺返回的時間字符串轉(zhuǎn)換為指定的格式時間再顯示在UI上,有興趣的可以了解一下。
    2017-04-04
  • iOS逆向教程之跟蹤函數(shù)調(diào)用詳解

    iOS逆向教程之跟蹤函數(shù)調(diào)用詳解

    這篇文章主要給大家介紹了關(guān)于iOS逆向教程之跟蹤函數(shù)調(diào)用的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。
    2018-04-04
  • Objective-C學習之ARC的實現(xiàn)方法

    Objective-C學習之ARC的實現(xiàn)方法

    自動引用計數(shù)(Automatic Reference Counting, ARC)把壓在程序員們肩頭的管理內(nèi)存的重擔卸除了不少,更不用說讓跟蹤內(nèi)存泄漏那樣的煩心事也少了很多。下面這篇文章主要給大家介紹了關(guān)于Objective-C學習之ARC的實現(xiàn)方法,需要的朋友可以參考借鑒下。
    2017-12-12

最新評論