在iOS開發(fā)中我們經(jīng)常會(huì)遇到一些業(yè)務(wù)平挑,需要延遲一段時(shí)間去做一件事扇谣,或者是每隔一段時(shí)間都去做一件事锣尉,這就需要用到定時(shí)任務(wù)處理。iOS開發(fā)中常用的定時(shí)任務(wù)實(shí)現(xiàn)方式如下:
- performSelector 實(shí)現(xiàn)延時(shí)任務(wù)
- sleep(10)線程掛起/[NSThread sleepForTimeInterval: 10]實(shí)現(xiàn)任務(wù)等待凳厢,會(huì)阻塞主線程
- GCD的dispatch_after實(shí)現(xiàn)延時(shí)或dispatch_source_set_timer定時(shí)任務(wù)
- NSTimer實(shí)現(xiàn)定時(shí)任務(wù)
- CADisplayLink實(shí)現(xiàn)定時(shí)任務(wù)(幀頻刷新)
一账胧、performSelector
延時(shí)任務(wù)可以通過當(dāng)前UIViewController的performSelector隱式創(chuàng)建子線程實(shí)現(xiàn),不會(huì)阻塞住主線程先紫。
[self performSelector:@selector(doSomething) withObject:nil afterDelay:10];
- (void)doSomething {
NSLog(@"%s",__func__);
}
一治泥、sleep/sleepForTimeInterval
使用它們可以實(shí)現(xiàn)后面任務(wù)的等待,但是會(huì)阻塞主線程遮精,還是慎用居夹。
//C語言掛起線程10秒
sleep(10);
//Objective-C的線程類方法
[NSThread sleepForTimeInterval:10];
三败潦、GCD(dispatch_after/dispatch_source_set_timer)
dispatch_after函數(shù)并不是在指定時(shí)間之后才開始執(zhí)行處理,而是在指定時(shí)間之后將任務(wù)追加到主隊(duì)列中准脂。嚴(yán)格來說劫扒,這個(gè)時(shí)間并不是絕對(duì)準(zhǔn)確的,但想要大致延遲執(zhí)行任務(wù)狸膏,dispatch_after函數(shù)是很有效的沟饥。
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC));
dispatch_after(delay, dispatch_get_main_queue(), ^{
// 2.0秒后異步追加任務(wù)代碼到主隊(duì)列,并開始執(zhí)行
NSLog(@"after---%@",[NSThread currentThread]); // 打印當(dāng)前線程
});
GCD還可以用來實(shí)現(xiàn)定時(shí)器功能湾戳,還能設(shè)置延時(shí)開啟計(jì)時(shí)器贤旷,使用中注意一定定義強(qiáng)引用指針來指向計(jì)時(shí)器對(duì)象才可以讓計(jì)時(shí)器生效。
@property (nonatomic,strong) dispatch_source_t timer;
//在指定線程上定義計(jì)時(shí)器
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);
//開始的時(shí)間
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
//設(shè)置計(jì)時(shí)器
dispatch_source_set_timer(_timer, when, 1.0 * NSEC_PER_SEC, 0);
//計(jì)時(shí)器回調(diào) block
dispatch_source_set_event_handler(_timer, ^{
NSLog(@"dispatch_source_set_timer is working");
});
//開啟計(jì)時(shí)器
dispatch_resume(_timer);
//強(qiáng)引用計(jì)時(shí)器對(duì)象
self.timer = _timer;
四砾脑、NSTimer
NSTimer主要用于開啟定時(shí)任務(wù)幼驶,但要正確使用才能保證它能夠正常有效地運(yùn)行。
尤其要注意以下幾點(diǎn):
- 確保NSTimer已添加到當(dāng)前Runloop韧衣,且當(dāng)前runloop已啟動(dòng)盅藻;
- 主線程的runloop默認(rèn)開啟的,只要timer添加到runloop就會(huì)執(zhí)行畅铭;
- 子線程的runloop默認(rèn)關(guān)閉的氏淑,timer添加到runloop后要手動(dòng)啟動(dòng)runloop ;
- NSTimer建議在子線程用顶瞒,因?yàn)橹骶€程要更新Ui影響時(shí)間準(zhǔn)確性夸政;
- scheduledTimerWithTimeInterval創(chuàng)建的timer會(huì)默認(rèn)添加到當(dāng)前RunLoop中;
- timerWithTimeInterval創(chuàng)建的timer需要手動(dòng)添加到線程當(dāng)前RunLoop中榴徐。
//timerWithTimeInterval:主線程創(chuàng)建timer,需要手動(dòng)添加到runloop中
NSTimer *mainThreadTimer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(mainThreadTimer_SEL) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:mainThreadTimer forMode:NSDefaultRunLoopMode];
//scheduledTimerWithTimeInterval:主線程中創(chuàng)建timer匀归,不需要手動(dòng)添加到runloop中
NSTimer *mainThreadSchduledTimer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(mainThreadSchduledTimer_SEL) userInfo:nil repeats:YES];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//timerWithTimeInterval:子線程創(chuàng)建timer坑资,需要手動(dòng)添加到runloop中,并啟動(dòng)runloop
NSTimer *subThreadTimer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(subThreadTimer_SEL) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:subThreadTimer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
//scheduledTimerWithTimeInterval:子線程創(chuàng)建timer穆端,不需要手動(dòng)添加到runloop中袱贮,但需要啟動(dòng)runloop
NSTimer *subThreadSchduledTimer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(subThreadSchduledTimer_SEL) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
});
五、CADisplayLink
CADisplayLink實(shí)現(xiàn)的定時(shí)器與屏幕刷新頻率綁定在一起体啰,是一種幀頻刷新攒巍,適用于界面的不斷重繪(例如流暢動(dòng)畫和視頻播放等)。
CADisplayLink需添加到當(dāng)前運(yùn)行的runloop中啟動(dòng)荒勇,默認(rèn)每幀都調(diào)用柒莉,可根據(jù)需求設(shè)置每幾幀調(diào)用一次。
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLink_SEL)];
//添加到當(dāng)前運(yùn)行的runloop中啟動(dòng)
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
//暫停沽翔、繼續(xù)對(duì)selector的調(diào)用
[displayLink setPaused:YES];
[displayLink setPaused:NO];
//設(shè)置每秒的幀數(shù)兢孝,默認(rèn)為0窿凤,60幀每秒,有效范圍1-60
[displayLink setPreferredFramesPerSecond:10];
//移除跨蟹,不再調(diào)用
[displayLink invalidate];
displayLink = nil;
參考:《iOS程序員面試筆試寶典》