最近閑來無事,寫一些之前用過的一些知識(shí)點(diǎn)也好做個(gè)總結(jié)贸人。這次來說說倒計(jì)時(shí)的實(shí)現(xiàn)也就是計(jì)時(shí)器婶溯。在剛接觸這一塊的時(shí)候也有不少資料文章鲸阔,不過沒有現(xiàn)在多,但是現(xiàn)在好多都是重復(fù)的沒什么好說的迄委。實(shí)現(xiàn)它目前基本上有三種方式1.NSTimer 2.GCD 3.CADisplayLink褐筛。
先說說NSTimer:
創(chuàng)建它也有兩種方式我這里用系統(tǒng)封裝好的一個(gè)代碼如下:
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
- (void)updateTimer {
_count--;
if (_count <= 0) {
[self.nstimerBtn setTitle:@"重新發(fā)送" forState:UIControlStateNormal];
self.nstimerBtn.userInteractionEnabled = YES;
[self.timer invalidate];
} else {
[self.nstimerBtn setTitle:[NSString stringWithFormat:@"%ld秒后開始",(long)_count] forState:UIControlStateNormal];
self.nstimerBtn.userInteractionEnabled = NO;
}
}
這種方式默認(rèn)已經(jīng)加入到Runloop中了,說到Runloop最近也在看有時(shí)間也會(huì)分享自己所學(xué)叙身。用NSTimer要注意釋放因?yàn)檎{(diào)用創(chuàng)建方法后渔扎,target對象的計(jì)數(shù)器會(huì)加1,直到執(zhí)行完畢信轿,自動(dòng)減1晃痴。如果是循環(huán)執(zhí)行的話,就必須手動(dòng)關(guān)閉财忽,否則可以不執(zhí)行釋放方法倘核。而針對我們何時(shí)釋放可以參考:使用 NSTimer 做定時(shí)效果需要注意的問題NSTimer
[self.timer invalidate];
他的特點(diǎn)有:1.有一定的延遲
2.會(huì)受到Runloop影響,因?yàn)槭褂蒙厦娴膭?chuàng)建方式即彪,會(huì)自動(dòng)把timer加入MainRunloop的NSDefaultRunLoopMode中紧唱。而當(dāng)頁面滑動(dòng)時(shí)是UITrackingRunLoopMode,當(dāng)然我們可以用NSRunLoopCommonModes但是有時(shí)還是會(huì)有其他問題以后會(huì)在Runloop總結(jié)中說明隶校。
再說GCD:
GCD的創(chuàng)建看起來挺復(fù)雜其實(shí)系統(tǒng)也有幫我們封裝好漏益,我們只需要傳入對應(yīng)的參數(shù)即可而我也把他封裝成了一下用起來也很方便,首先說一下各個(gè)參數(shù)對應(yīng)的含義:
/*
第一個(gè)參數(shù):創(chuàng)建source的類型 DISPATCH_SOURCE_TYPE_TIMER:定時(shí)器
第二個(gè)參數(shù):0
第三個(gè)參數(shù):0
第四個(gè)參數(shù):隊(duì)列
*/
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
其中的隊(duì)列需要我們手動(dòng)創(chuàng)建:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
然后設(shè)置時(shí)間等:
/*
第一個(gè)參數(shù):定時(shí)器對象
第二個(gè)參數(shù):DISPATCH_TIME_NOW 表示從現(xiàn)在開始計(jì)時(shí)
第三個(gè)參數(shù):間隔時(shí)間 GCD里面的時(shí)間最小單位為納秒
第四個(gè)參數(shù):精準(zhǔn)度(表示允許的誤差,0表示絕對精準(zhǔn))
*/
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
然后再掉用任務(wù):
dispatch_source_set_event_handler(timer, ^{
});
最后啟動(dòng)任務(wù):
dispatch_resume(timer);
當(dāng)然在我們不使用任務(wù)時(shí)也不要忘記釋放
dispatch_source_cancel(timer);
而我最后對倒計(jì)時(shí)封裝后為:
+ (void)initWithGCD:(int)timeValue beginState:(void (^)(NSInteger time))begin endState:(void (^)(void))end {
__block NSInteger time = timeValue;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0);
dispatch_source_set_event_handler(timer, ^{
if (time <= 0) {
dispatch_source_cancel(timer);
dispatch_async(dispatch_get_main_queue(), ^{
if (end) {
end();
}
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
if (begin) {
begin(time);
}
});
time--;
}
});
dispatch_resume(timer);
}
我們可以這樣使用他
[CJXTimer initWithGCD:60 beginState:^(NSInteger seconds){
[self.gcdBtn setTitle:[NSString stringWithFormat:@"%ld秒后開始",(long)seconds] forState:UIControlStateNormal];
self.gcdBtn.userInteractionEnabled = NO;
NSLog(@"%ld",(long)seconds);
} endState:^() {
[self.gcdBtn setTitle:@"重新發(fā)送" forState:UIControlStateNormal];
self.gcdBtn.userInteractionEnabled = YES;
}];
由于swift3.0對GCD寫法改動(dòng)還挺大有時(shí)間實(shí)現(xiàn)一下swift版本深胳,對于GCD創(chuàng)建的優(yōu)點(diǎn)很明顯精度高绰疤,不受Runloop影響。
最后說一下CADisplayLink
首先我們先看一下他的創(chuàng)建步驟:
// 創(chuàng)建displayLink
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(test:)];
// 將創(chuàng)建的displaylink添加到runloop中舞终,否則定時(shí)器不會(huì)執(zhí)行
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
// 停止定時(shí)器
[displayLink invalidate];
displayLink = nil;
我們會(huì)發(fā)現(xiàn)和NSTimer有點(diǎn)類似轻庆,但是他們的實(shí)現(xiàn)方式不同:CADisplayLink是CoreAnimation提供的另一個(gè)類似于NSTimer的類,它總是在屏幕完成一次更新之前啟動(dòng)权埠,它的接口設(shè)計(jì)的和NSTimer很類似榨了,所以它實(shí)際上就是一個(gè)內(nèi)置實(shí)現(xiàn)的替代,但是和timeInterval以秒為單位不同攘蔽,看很多文章提到CADisplayLink中有一個(gè)frameInterval屬性很重要他指定了間隔多少幀之后才執(zhí)行,默認(rèn)值是1呐粘,意味著每次屏幕更新之前都會(huì)執(zhí)行一次满俗。但是如果動(dòng)畫的代碼執(zhí)行起來超過了六十分之一秒转捕,你可以指定frameInterval為2,就是說動(dòng)畫每隔一幀執(zhí)行一次(一秒鐘30幀)或者3唆垃,也就是一秒鐘20次五芝,等等,但是現(xiàn)在我寫這個(gè)屬性已經(jīng)有提示了已經(jīng)有新的屬性來代替就是preferredFramesPerSecond但是他的默認(rèn)值是0對于他的解釋目前還不是很清楚辕万。而對于他的應(yīng)用可以參考:基于 CADisplayLink 的 FPS 指示器詳解
使用計(jì)時(shí)器做商品倒計(jì)時(shí)可能會(huì)出現(xiàn)系統(tǒng)時(shí)間不準(zhǔn)確等問題可以參考:簡單實(shí)現(xiàn)一個(gè)定時(shí)器管理
總結(jié)以上其實(shí)NSTimer已經(jīng)可以滿足我們大部分情況的使用枢步,當(dāng)然我們可以根據(jù)不同的需求來選擇不同的實(shí)現(xiàn)。