在日常開發(fā)中,我們經(jīng)常要延時(shí)等待(如網(wǎng)絡(luò)請(qǐng)求素邪,UI更新完)然后做一些事情揭朝,或者是做一些周期性的處理。這個(gè)時(shí)候就要求我們實(shí)現(xiàn)一個(gè)周期性的定時(shí)器進(jìn)行延時(shí)操作欣除。常用的延時(shí)實(shí)現(xiàn)方法有下面三種:
1.NStimer實(shí)現(xiàn)
不管是一次性的還是周期性的timer的實(shí)際觸發(fā)事件的時(shí)間住拭,都會(huì)與所加入的RunLoop和RunLoop Mode有關(guān),如果此RunLoop正在執(zhí)行一個(gè)連續(xù)性的運(yùn)算历帚,timer就會(huì)被延時(shí)觸發(fā)滔岳。重復(fù)性的timer遇到這種情況,如果延遲超過了一個(gè)周期挽牢,則會(huì)在延時(shí)結(jié)束后立刻執(zhí)行谱煤,并按照之前指定的周期繼續(xù)執(zhí)行。(PS: 所以有時(shí)使用timer禽拔,會(huì)偶爾出現(xiàn)連續(xù)兩次刷新的情況刘离,可能上一次timer被阻塞)
NSTimer *timer =[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(changeUI) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
上面方式:每隔一秒調(diào)用一次定時(shí)器的block
使用注意:1. 這個(gè)runLoop運(yùn)行模式,建議使用NSRunLoopCommonModes睹栖;使用NSDefaultRunLoopMode硫惕,在屏幕上有UI更新(如ScrollView滑動(dòng)時(shí)),runLoop就會(huì)被切換到其他的模式(NSEventTrackingRunLoopMode)野来,此時(shí)timer不會(huì)被執(zhí)行恼除。NSRunLoopCommonModes中包含了NSEventTrackingRunLoopMode,timer照樣被執(zhí)行
2.當(dāng)不在使用這個(gè)timer時(shí)曼氛,需要手動(dòng)調(diào)用invalidate方法將timer釋放并重置為nil豁辉。因?yàn)樵趧?chuàng)建timer時(shí),會(huì)默認(rèn)對(duì)target進(jìn)行一次retain搪锣,如果不手動(dòng)釋放timer秋忙,target對(duì)象也得不到釋放會(huì)造成內(nèi)存泄露
3.主線程開啟,回調(diào)的selector默認(rèn)也在主線程執(zhí)行构舟。如果想要多線程執(zhí)行timer灰追,需要手動(dòng)開啟一個(gè)NSthread,值得主要的是:由于子線程的runLoop默認(rèn)是關(guān)閉的狗超,所以需要手動(dòng)開啟子線程的runLoop弹澎,否則timer永遠(yuǎn)不會(huì)被執(zhí)行
//創(chuàng)建并執(zhí)行新的線程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(newThread) object:nil];
[thread start];
//在新線程開啟定時(shí)器
- (void)openTimer
@autoreleasepool
{
//在當(dāng)前Run Loop中添加timer,模式是默認(rèn)的NSDefaultRunLoopMode
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(timer_callback) userInfo:nil repeats:YES];
//開始執(zhí)行新線程的Run Loop
[[NSRunLoop currentRunLoop] run];
}
}
2.CADisplayLink:
frameInterval :NSInteger類型的值努咐,用來設(shè)置間隔多少幀調(diào)用一次selector方法苦蒿,默認(rèn)值是1,即每幀都調(diào)用一次渗稍。
duration:readOnly的CFTimeInterval值佩迟,表示兩次屏幕刷新之間的時(shí)間間隔团滥。需要注意的是,該屬性在target的selector被首次調(diào)用以后才會(huì)被賦值报强。selector的調(diào)用間隔時(shí)間計(jì)算方式是:調(diào)用間隔時(shí)間 = duration * frameInterval (調(diào)用一次方法刷新幀數(shù) *刷新每一幀所需的時(shí)間)
CADisplayLink的使用跟timer比較類似灸姊,這里就不詳細(xì)介紹。朋友們可以參考參考下面這個(gè)鏈接秉溉。
http://blog.csdn.net/ch_soft/article/details/9408855
3.GCD實(shí)現(xiàn)Timer
GCD中的Timer應(yīng)該是最靈活的力惯,而且是多線程的。GCD中的Timer是靠Dispatch Source來實(shí)現(xiàn)的召嘶。
最近使用GCD的timer做一個(gè)驗(yàn)證碼發(fā)送的例子父晶。整個(gè)使用過程分為五步,代碼如下:
__block NSInteger time = 59; //倒計(jì)時(shí)時(shí)間
//1.創(chuàng)建一個(gè)全局并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//2.創(chuàng)建一個(gè)timer加到隊(duì)列中
dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//3.設(shè)置timer的執(zhí)行周期弄跌,最后一個(gè)參數(shù)表示延時(shí)秒數(shù)為0甲喝,盡可能精確
dispatch_source_set_timer(_timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0);
//4.設(shè)置timer周期執(zhí)行的block
dispatch_source_set_event_handler(_timer, ^{
if(time <= 0){ //倒計(jì)時(shí)結(jié)束,關(guān)閉定時(shí)器碟绑,還原UI
dispatch_source_cancel(_timer);
dispatch_async(dispatch_get_main_queue(), ^{
//設(shè)置按鈕的樣式
[self.getMsgBtn setTitle:@"獲取驗(yàn)證碼" forState:UIControlStateNormal];
[self.getMsgBtn setTitleColor:ZGColor(25, 111, 255) forState:UIControlStateNormal];
[self.getMsgBtn setBackgroundColor:[UIColor whiteColor]];
self.getMsgBtn.titleLabel.font = [UIFont systemFontOfSize: 14.0];
self.getMsgBtn.userInteractionEnabled = YES;
});
}else{ //否則繼續(xù)計(jì)時(shí)
int leftSeconds = time % 60;
dispatch_async(dispatch_get_main_queue(), ^{
//設(shè)置按鈕顯示讀秒效果
[self.getMsgBtn setTitle:[NSString stringWithFormat:@"%d秒后重新發(fā)送",leftSeconds] forState:UIControlStateNormal];
[self.getMsgBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[self.getMsgBtn setBackgroundColor:[UIColor lightGrayColor]];
self.getMsgBtn.titleLabel.font = [UIFont systemFontOfSize: 13.0];
self.getMsgBtn.userInteractionEnabled = NO;
});
time--;
}
});
//5.啟動(dòng)timer
dispatch_resume(_timer);
使用注意:由于dispatch_source_t 創(chuàng)建后默認(rèn)是暫停狀態(tài)俺猿,需要手動(dòng)開啟才會(huì)執(zhí)行茎匠。
另外格仲,實(shí)際開發(fā)中有需求:等待reload執(zhí)行完刷新完tableView,然后做一些操作
想要程序延遲到reloadData結(jié)束在操作诵冒,可以用以下方法
方法1:
[self.tableView reloadData];
[self.tableView layoutIfNeeded];
//刷新完成
…….
layoutIfNeeded會(huì)強(qiáng)制重繪并等待完成凯肋。
方法2:
[objc] view plain copy
[self.tableView reloadData];
dispatch_sync(dispatch_get_main_queue(), ^{
//刷新完成
});
reloadData會(huì)在主隊(duì)列執(zhí)行,而dispatch_get_main_queue會(huì)等待機(jī)會(huì)汽馋,直到主隊(duì)列空閑才執(zhí)行侮东。