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