標籤:
timer,計時器,就是用來計時的,可以將它和要處理的動作綁定起來,讓這個動作在某段時間之後執行,或者周期性地執行。
一、timer的工作原理
timer的工作和run loop密不可分,由於平常我們使用Application Kit和UIKit來建立的app,在app的主線程啟動的時候就自動啟動了一個runloop,因此在主線程中使用timer感覺不到runloop的存在。如果要在分線程中使用timer,就必須要瞭解timer和runloop的協同工作原理,自己去建立runloop,讓它和timer一起配合工作。
簡短概括runloop和timer的工作關係就是,timer只負責計時,timer所綁定的動作,是由runloop來執行的。下面就來詳細地講講這種關係。
我們知道,runloop啟動並執行時候需要指定運行模式(run loop mode),runloop的運行模式規定了它要監聽的事件來源,以及事件發生時它要通知的對象。因此可以給runloop定義多種運行模式,runloop運行時,可以同時啟用多個運行模式。
一個timer同一時間只可以註冊到一個runloop中,但是可以添加到這個runloop的多個運行模式中。(表示了這個關係)
初始化一個timer,就是給timer綁定一個要執行的動作;登記一個時間段,這個時間段用來告訴runloop,該動作需要在該時間段過去之後執行,或者每隔這個時間段執行一次。
在timer與runloop的協同工作中,timer只負責計時,而runloop負責監視其所啟用的運行模式中添加的timer是否已經達到了其初始化時登記的時間,一旦發現某個timer已經到達了這個時間,就會去執行該timer所綁定的動作(這就是所謂的fire the timer)。
由此可以見得,timer所登記的這個時間段並不是動作的絕對執行時間。timer綁定的動作具體在什麼時候執行,要看runloop是否在運行,且是否啟用了timer所加入的運行模式,還要看runloop正在處理的東西多不多,能不能及時發現timer已經計時完畢。
二、timer的具體使用1、建立timer(1)使用當前的runloop來建立一個timer
方法:
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
參數TimeInterval是timer計時的周期長度,每隔時間長度TimeInterval,計時器就會清零一次。
這兩個方法,完成一系列動作:
- 建立timer;
- 將它加入到當前runloop的預設運行模式中;
- 讓timer開始計時。
對於這種方式添加的timer,runloop就會等它的計時時間長度第一次達到TimeInterval時,才會執行它綁定的動作。
(2)自己建立一個timer,之後將它註冊到某個runloop中
方法:
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
這兩個方法可以返回一個timer。
之後要用runloop對象的方法將timer註冊到這個runloop對象的某個運行模式中:
-(void)addTimer:(nonnull NSTimer *) forMode:(nonnull NSString *);
這樣添加到runloop的timer會立即開始計時。
(3)初始化一個timer,並指明它綁定的動作被執行的時間點
方法:
- (instanceType)initWithFireDate:(nonnull NSDate *) interval:(NSTimeInterval) target:(nonnull id) selector:(nonnull SEL) userInfo:(nullable id) repeats:(BOOL);
這裡的fireDate是個時間點,是相對於系統時間的,只要到達了這個時間點,runloop就會去執行timer綁定的動作。所以如果timer是一次性的,都不需要計時,interval這個參數就沒有作用了。如果是重複的timer,才需要計時,參數interval才有用。
可以用setFireDate方法來給timer設定動作的執行時間。
2、停止timer
timer需要在runloop的管理下才會有效,停止timer,只要把它從它所在的runloop中清理掉就可以實現。(或者讓runloop停止運行)。
(1)對於一次性的timer
一次性的timer都不用考慮這個問題,因為runloop執行完timer綁定的動作就會自動把這個timer從它所在的運行模式中清理掉。
(2)對於周期性的timer
方法:
-(void)invalidate;
對於周期性的timer才需要考慮這個問題。
要停止一個周期性的timer,要擷取這個timer對象的引用,在需要清理timer的時候,主動調用該timer對象的invalidate方法即可。
當然,也可以對一次性的timer執行這個方法,在timer的動作被runloop執行之前調用invalidate方法,timer的動作就永遠不會被執行了。
iOS timer計時器