定時(shí)器是什么?
定時(shí)器提供執(zhí)行延遲動(dòng)作或周期性動(dòng)作的方式。 定時(shí)器等待直到一定時(shí)間間隔過去,然后觸發(fā),向指定的對(duì)象發(fā)送指定的消息。 例如,您可以創(chuàng)建一個(gè)計(jì)時(shí)器,向控制器對(duì)象發(fā)送消息,告訴它在一定時(shí)間間隔后更新特定值。
注意:定時(shí)器與NSRunLoop對(duì)象協(xié)同工作。 因此,他們不提供實(shí)時(shí)機(jī)制 - 他們的準(zhǔn)確性有限。
定時(shí)器的精度
定時(shí)器不是實(shí)時(shí)機(jī)制;它僅在已經(jīng)添加了定時(shí)器的運(yùn)行循環(huán)模式中的一個(gè)正在運(yùn)行時(shí)才觸發(fā),并且能夠檢查定時(shí)器的觸發(fā)時(shí)間是否已經(jīng)過去。由于典型的運(yùn)行循環(huán)管理的各種輸入源,用于定時(shí)器的時(shí)間間隔的有效分辨率被限制在大約50-100毫秒。如果定時(shí)器的觸發(fā)時(shí)間發(fā)生在運(yùn)行循環(huán)處于不監(jiān)視定時(shí)器或長(zhǎng)度調(diào)出的模式時(shí),則定時(shí)器不會(huì)觸發(fā),直到下一次運(yùn)行循環(huán)檢查定時(shí)器。因此,定時(shí)器可能發(fā)生的實(shí)際時(shí)間可以是預(yù)定點(diǎn)火時(shí)間之后的顯著時(shí)間段。
重復(fù)定時(shí)器基于預(yù)定的點(diǎn)火時(shí)間而不是實(shí)際的點(diǎn)火時(shí)間重新調(diào)度自身。例如,如果定時(shí)器被調(diào)度為在特定時(shí)間并且在那之后每5秒觸發(fā),則即使實(shí)際點(diǎn)火時(shí)間被延遲,預(yù)定點(diǎn)火時(shí)間將總是落在原始的5秒時(shí)間間隔上。如果點(diǎn)火時(shí)間被延遲,使得它通過一個(gè)或多個(gè)預(yù)定點(diǎn)火時(shí)間,則定時(shí)器在該時(shí)間段僅點(diǎn)火一次;則在點(diǎn)火之后對(duì)于未來的下一個(gè)預(yù)定點(diǎn)火時(shí)間重新計(jì)劃定時(shí)器。
定時(shí)器的替代品
如果您只是想在將來的某個(gè)時(shí)間發(fā)送消息,可以不使用計(jì)時(shí)器。 您可以使用performSelector:withObject:afterDelay:和相關(guān)方法直接在另一個(gè)對(duì)象上調(diào)用方法。 另外有一些變體,如performSelectorOnMainThread:withObject:waitUntilDone :,允許您調(diào)用特定線程上的方法。 您還可以使用cancelPreviousPerformRequestsWithTarget:和相關(guān)方法取消延遲信息的發(fā)送。
創(chuàng)建定時(shí)器
創(chuàng)建定時(shí)器有三種方法:
1. 使用當(dāng)前運(yùn)行循環(huán)創(chuàng)建定時(shí)器
+ (NSTimer*)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation*)invocation repeats:(BOOL)yesOrNo;?
+ (NSTimer*)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullableid)userInfo repeats:(BOOL)yesOrNo;這種最常見
iOS10之后又出現(xiàn)了下面這種block的方式:
+ (NSTimer*)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void(^)(NSTimer*timer))block
2. 創(chuàng)建一個(gè)定時(shí)器,之后主動(dòng)添加到運(yùn)行運(yùn)行中
+ (NSTimer*)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation*)invocation repeats:(BOOL)yesOrNo;
+ (NSTimer*)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullableid)userInfo repeats:(BOOL)yesOrNo;
iOS10之后又出現(xiàn)了下面這種block的方式:
+ (NSTimer*)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void(^)(NSTimer*timer))block
?3. 使用給定的啟動(dòng)日期初始化定時(shí)器
- (instancetype)initWithFireDate:(NSDate*)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(nullableid)ui repeats:(BOOL)rep
iOS10之后又出現(xiàn)了下面這種block的方式:
- (instancetype)initWithFireDate:(NSDate*)date interval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void(^)(NSTimer*timer))block
引用定時(shí)器和對(duì)象聲明周期
如果用第一種方式創(chuàng)建的非循環(huán)定時(shí)器,從對(duì)象生命周期的角度來看,通常沒有必要保留對(duì)這種定時(shí)器的引用,因?yàn)槎〞r(shí)器執(zhí)行完畢之后會(huì)自動(dòng)結(jié)束生命周期。 然而,在許多情況下,你也想主動(dòng)讓定時(shí)器無效。在這種情況下,您需要保留對(duì)定時(shí)器的引用,以便您可以在適當(dāng)?shù)臅r(shí)候停止定時(shí)器。如果是用第一種方式創(chuàng)建的是循環(huán)定時(shí)器,那么需要保留對(duì)定時(shí)器的引用,以便你隨時(shí)可以停止定時(shí)器并釋放器內(nèi)存。如果用第二種方式創(chuàng)建的定時(shí)器,則必須保持對(duì)定時(shí)器的強(qiáng)引用,以便在使用定時(shí)器之前不會(huì)釋放定時(shí)器對(duì)象。
特別說明:定時(shí)器保持對(duì)其目標(biāo)的強(qiáng)引用。這意味著只要定時(shí)器保持有效,其目標(biāo)將不會(huì)被釋放。作為推論,這意味著定時(shí)器的目標(biāo)在dealloc方法中嘗試讓定時(shí)器無效是沒有意義的 ,只要定時(shí)器有效,dealloc方法就不會(huì)被調(diào)用。
定時(shí)器容差
在iOS 7或更高版本,您可以指定計(jì)時(shí)器的公差(tolerance)。允許系統(tǒng)在定時(shí)器觸發(fā)時(shí)的靈活性提高,系統(tǒng)優(yōu)化以增加功率節(jié)省和響應(yīng)性的能力。定時(shí)器可以在其計(jì)劃的觸發(fā)日期和計(jì)劃的觸發(fā)日期加上公差之間的任何時(shí)間觸發(fā)。定時(shí)器在預(yù)定的觸發(fā)日期之前不會(huì)觸發(fā)。對(duì)于重復(fù)定時(shí)器,下一個(gè)觸發(fā)日期是從原始觸發(fā)日期計(jì)算的,而不考慮在個(gè)別觸發(fā)時(shí)間之間的容差,以避免漂移。tolerance的默認(rèn)值為零,這意味著不使用tolerance這個(gè)屬性。無論tolerance屬性的值如何,系統(tǒng)都會(huì)保留對(duì)特定計(jì)時(shí)器應(yīng)用少量容差的權(quán)利。
作為定時(shí)器的用戶,您將最好地了解定時(shí)器的適當(dāng)容限。一般的經(jīng)驗(yàn)法則是,對(duì)于重復(fù)定時(shí)器,將容差設(shè)置為間隔的至少10%。即使少量的容差也會(huì)對(duì)應(yīng)用程序的功耗產(chǎn)生顯著的積極影響。系統(tǒng)可以設(shè)置公差的最大值。