先說一下我的業務需求,最近在做一個小項目,需要用到定時器的功能,NSTimer類,期間,出現了一些小問題,不過最終通過自己的努力,終於做出來了。我想總結一下,我對NSTimer類的學習和理解。
不多說了,先上效果圖
界面元素很簡單,兩個UIButton 開始和暫停,20表示起始倒計時。最終的效果是,按開始按鈕的時候,倒計時開始運行,按暫停按鈕的時候,計時器,停止倒計時。當倒計時爲0的時候,彈出一個對話框,提示時間已到。
業務需求很簡單,但是,在我的實現中,卻出現了,一些小錯誤。 主要是暫停鍵不能點擊多次,開始鍵也不能點擊多次,我相信,剛開始,接觸這個NSTimer的人,也會出現這幾個問題。
直接上幾個主要的代碼:
控制器類的.h文件中
@interface sdsViewController : UIViewController<UIAlertViewDelegate>
//定義一個定時器,做爲實例變量
@property(nonatomic,retain) NSTimer *timer;
//顯示倒計時當前狀態
@property (retain, nonatomic) IBOutlet UILabel *timeDisplay;
//開始按鈕,響應的action
- (IBAction)startTime:(id)sender;
//暫停按鈕響應的action
- (IBAction)stopTime:(id)sender;
@end
.m中關鍵代碼
開始按鈕 響應代碼:
- (IBAction)startTime:(id)sender {
//如果定時器對象不存在,則創建一個並啓動
if(!_timer){
//創建一個定時器,這個是直接加到當前消息循環中,注意與其他初始化方法的區別
_timer=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(changeTimeAtTimedisplay) userInfo:nil repeats:YES];
// [_timer fire]; //對於這個fire方法,稍後會詳解,它不是啓動一個定時器,這麼簡單
}
}
//結束按鈕響應代碼:
- (IBAction)stopTime:(id)sender {
if (_timer) {
NSLog(@"調用 self.time爲真!!");
//如果定時器在運行
if ([self.timer isValid]) {
NSLog(@"單擊停止按鈕,取消定時器!!");
[self.timer invalidate];
//這行代碼很關鍵
_timer=nil;
}
}
}
一切OK,現在分析程序用到的關鍵地方。
先看看NSTimer類的結構,比較簡單
Tasks
Creating a Timer
//創建一個定時器 ,以下是便利構造器方法,都懂的
+ scheduledTimerWithTimeInterval:invocation:repeats:
+ scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
+ timerWithTimeInterval:invocation:repeats:
+ timerWithTimeInterval:target:selector:userInfo:repeats:
//初始化方法
– initWithFireDate:interval:target:selector:userInfo:repeats:
//是開始一個定時器嗎,恐怕沒那麼簡單????????
Firing a Timer
– fire
//是暫停一個定時器嗎,NO ,是Stop ,寫的很清楚
Stopping a Timer
– invalidate
//關於定時器的以下信息
Information About a Timer
– isValid //是否在運行
– fireDate //Returns the date at which the receiver will fire.
– setFireDate: //重新設置定時器開始運行的時間
– timeInterval //定時器延時時間
– userInfo //其他信息
先說一下,初始化方法
+ scheduledTimerWithTimeInterval:invocation:repeats:
+ scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
這兩個是創建一個定時器,並加入到當前運行循環中,即我們可以這樣去理解,這樣初始化一個定時器時,在(NSTimeInterval)seconds 時間之後,自動啓動定時器。
而以下兩個初始化方法這不一樣:
+ timerWithTimeInterval:invocation:repeats:
+ timerWithTimeInterval:target:selector:userInfo:repeats:
這兩個同樣是創建,但沒有加入到,運行循環中。class method to create the timer object without scheduling it on a run loop.然後,建立之後,必須(after creating it, you must add the timer to a run loop manually by calling the addTimer:forMode: method of the corresponding NSRunLoop object.),這就是與上面兩個方法的區別。
//創建一個定時器
_timer=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(changeTimeAtTimedisplay) userInfo:nil repeats:YES];
_timer=[NSTimer timerWithTimeInterval:10 target:self selector:@selector(changeTimeAtTimedisplay) userInfo:nil repeats:YES];
//必須手動加入到當前循環中去
NSRunLoop *runloop=[NSRunLoop currentRunLoop];
[runloop addTimer:_timer forMode:NSDefaultRunLoopMode];
以上兩端代碼效果是一樣的
關於這個方法:
Firing a Timer
– fire
其實他並不是真的啓動一個定時器,從之前的初始化方法中我們也可以看到,建立的時候,在適當的時間,定時器就會自動啓動。那這個方法是幹什麼的呢。
You can use this method to fire a repeating timer without interrupting its regular firing schedule. If the timer is non-repeating, it is automatically invalidated after firing, even if its scheduled fire date has not arrived.
這是官方文檔的說法,英文說的很清楚,但我們理解還不是很到位,爲了徹底搞懂它的功能。我又做了一個測試。
也是很簡單,把上面那個定時器,改變一點
//初始化的時候創建一個定時器
- (id) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
//創建一個定時器,
_timer=[NSTimer timerWithTimeInterval:10 target:self selector:@selector(changeTimeAtTimedisplay) userInfo:nil repeats:YES];
//手動加入到循環中
NSRunLoop *runloop=[NSRunLoop currentRunLoop];
[runloop addTimer:_timer forMode:NSDefaultRunLoopMode];
//當然這個定時器會自動啓動,只不多過了十秒之後,才觸發
}
return self
}
當我們單擊“開始”按鈕時,
- (IBAction)startTime:(id)sender {
//只是簡單地調用一下這個方法,看到底功能是什麼
[_timer fire];
}
結果是,單擊一下按鈕,倒計時減1,單擊一下減1,即它把觸發的時間給提前了,但過十秒後倒計時還會減1,即它只是提前觸發定時器,而不影響之前的那個定時器設置的時間,就好比我們等不及要去看一場球賽,趕緊把車開快些一樣,fire的功能就像讓我們快些到球場,但卻不影響球賽開始的時間。
還記得之前那個初始化定時器時,設置的是YES嗎,當我們,改爲NO時,即不讓它循環觸發時,我們此時再單擊開始按鈕。會猛然發現,倒計時減1了,但當我們再點擊開始按鈕時,會發現倒計時,不會動了。原因是:我們的定時器,被設置成只觸發一次,再fire的時候,觸發一次,該定時器,就被自動銷燬了,以後再fire也不會觸發了。
現在 我們再看官方的解釋,或許就會更明白了,
You can use this method to fire a repeating timer without interrupting its regular firing schedule. If the timer is non-repeating, it is automatically invalidated after firing, even if its scheduled fire date has not arrived.
這就對了,fire並不是啓動一個定時器,只是提前觸發而已。