iOS 處理定時(shí)任務(wù)常用方法

在項(xiàng)目開(kāi)發(fā)中鲫剿,經(jīng)常會(huì)在代碼中處理一些需要延時(shí)或定時(shí)執(zhí)行的任務(wù),iOS 中處理定時(shí)任務(wù)的方法包括 performSelector 方法稻轨、NSTimer灵莲、GCD、CADisplayLink殴俱,其本質(zhì)都是通過(guò)RunLoop來(lái)實(shí)現(xiàn)政冻,下面我們就對(duì)這幾個(gè)方法做一些總結(jié)。

1. performSelector方法

在NSRunLoop.h中有對(duì)NSObject類(lèi)的擴(kuò)展方法线欲,簡(jiǎn)單易用:

@interface NSObject (NSDelayedPerforming)

- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes;
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;

+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(nullable id)anArgument;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;

@end
2. NSTimer

NSTimer 是最常使用的定時(shí)器明场,使用方式簡(jiǎn)單,NSTimer 是也通過(guò)添加到RunLoop中被觸發(fā)并進(jìn)行工作的李丰,橋接 CFRunLoopTimerRef苦锨。NSTimer中定義的常用方法如下:

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
+ (NSTimer *)scheduledTimerWithTimeInterval:(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;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));

- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(nullable id)ui repeats:(BOOL)rep NS_DESIGNATED_INITIALIZER;

以下是初始化NSTimer的不同方式:

// 自動(dòng)加入currentRunLoop
self.timer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(timerRuning) userInfo:nil repeats:YES];
//self.timer = [NSTimer scheduledTimerWithTimeInterval:5.0 repeats:YES block:^(NSTimer * _Nonnull timer) { }];

// 手動(dòng)加入RunLoop
self.timer = [NSTimer timerWithTimeInterval:5 target:self selector:@selector(timerRuning) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];

// 指定timer觸發(fā)時(shí)刻
NSTimeInterval timeInterval = [self timeIntervalSinceReferenceDate] + 30;
NSDate *newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:timeInterval];
self.timer = [[NSTimer alloc] initWithFireDate:newDate interval:5 target:self selector:@selector(timerRuning) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];

如果當(dāng)前界面中有UITableView,則在 UITableView 在滾動(dòng)過(guò)程中趴泌,上述代碼中的定時(shí)器到了時(shí)間并沒(méi)有觸發(fā)逆屡。根據(jù)RunLoop的相關(guān)知識(shí),同一時(shí)刻 RunLoop 只運(yùn)行在一種 Mode 上踱讨,并且只有這個(gè) Mode 相關(guān)聯(lián)的源或定時(shí)器會(huì)被傳遞消息魏蔗,mainRunLoop 一般處于 NSDefaultRunLoopMode,但是在滾動(dòng)或者點(diǎn)擊事件等觸發(fā)時(shí)痹筛,mainRunLoop 切換至 NSEventTrackingRunLoopMode 莺治,而上面 timer 被加入的正是 NSDefaultRunLoopMode (未指明也默認(rèn)加入默認(rèn)模式),所以滑動(dòng)時(shí)未觸發(fā)定時(shí)操作帚稠。
解決方法:添加timer到mainRunLoop的NSRunLoopCommonMode中或者子線程中谣旁,需要注意的是加入子線程時(shí)要手動(dòng)開(kāi)啟并運(yùn)行子線程的RunLoop。

self.timer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(timerRuning) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

NSRunLoopCommonMode這是一組可配置的常用模式滋早。將輸入源與此模式相關(guān)聯(lián)也會(huì)將其與組中的每個(gè)模式相關(guān)聯(lián)榄审。對(duì)于Cocoa應(yīng)用程序,此集合默認(rèn)包括NSDefaultRunLoopMode杆麸,NSPanelRunLoopMode和NSEventTrackingRunLoopMode搁进。

注意:

  1. iOS10以前初始化的timer在運(yùn)行期間會(huì)對(duì)target進(jìn)行持有浪感,因此,在釋放時(shí)需要手動(dòng)調(diào)用invalidate方法饼问,并置nil影兽;
  2. timer不能在當(dāng)前宿主的dealloc方法中調(diào)用,因?yàn)閠imer沒(méi)有被釋放前莱革,當(dāng)前宿主不會(huì)執(zhí)行dealloc方法峻堰;
  3. 當(dāng)前RunLoop會(huì)切換Mode,因此可能導(dǎo)致timer不是立刻被觸發(fā)盅视。
  4. 在同一線程中捐名,timer重復(fù)執(zhí)行期間,有其他耗時(shí)任務(wù)時(shí)闹击,在改耗時(shí)任務(wù)完成前也不會(huì)觸發(fā)定時(shí)镶蹋,在耗時(shí)任務(wù)完成后,timer的定時(shí)任務(wù)會(huì)繼續(xù)執(zhí)行拇砰。
  5. dispatch_source_set_timer中設(shè)置啟動(dòng)時(shí)間梅忌,dispatch_time_t可通過(guò)兩個(gè)方法生成:dispatch_time 和 dispatch_walltime
3. GCD定時(shí)器

我們也可以通過(guò)GCD中的方法實(shí)現(xiàn)定時(shí)器來(lái)處理定時(shí)任務(wù)狰腌,實(shí)現(xiàn)的代碼邏輯如下:

// 1. 創(chuàng)建 dispatch source除破,指定檢測(cè)事件為定時(shí)
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue("Timer_Queue", 0));
// 2. 設(shè)置定時(shí)器啟動(dòng)時(shí)間、間隔
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC,  0 * NSEC_PER_SEC); 
// 3. 設(shè)置callback
dispatch_source_set_event_handler(timer, ^{
        NSLog(@"timer fired");
    });
dispatch_source_set_event_handler(timer, ^{
       //取消定時(shí)器時(shí)一些操作
    });
// 4. 啟動(dòng)定時(shí)器(剛創(chuàng)建的source處于被掛起狀態(tài))
dispatch_resume(timer);
// 5. 暫停定時(shí)器
dispatch_suspend(timer);
// 6. 取消定時(shí)器
dispatch_source_cancel(timer);
timer = nil;

當(dāng)我們想要timer只是延時(shí)執(zhí)行一次時(shí)琼腔,只調(diào)用以下方法即可:

// 在主線程中延時(shí)5s中執(zhí)行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
    });

注意:

  1. 正在執(zhí)行的 block瑰枫,在調(diào)用dispatch_suspend(timer)時(shí),當(dāng)前block并不會(huì)立即停止而是繼續(xù)執(zhí)行至完成丹莲;
  2. dispatch source在掛起時(shí)光坝,直接設(shè)置為 nil 或者重新賦值都會(huì)造成crash,需要在activate的狀態(tài)下調(diào)用dispatch_source_cancel(timer)后置為 nil 或者重新賦值甥材;
  3. dispatch_source_cancel方法可以在dispatch_source_set_event_handler中調(diào)用盯另,即timer可內(nèi)部持有也可外部持有;
  4. dispatch_resume和dispatch_suspend調(diào)用需成對(duì)出現(xiàn)洲赵,否則會(huì)crash鸳惯;
  5. dispatch source會(huì)比 NSTimer 更精準(zhǔn)一些。

參考文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末叠萍,一起剝皮案震驚了整個(gè)濱河市芝发,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌苛谷,老刑警劉巖辅鲸,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異腹殿,居然都是意外死亡独悴,警方通過(guò)查閱死者的電腦和手機(jī)例书,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)绵患,“玉大人雾叭,你說(shuō)我怎么就攤上這事÷潋” “怎么了织狐?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)筏勒。 經(jīng)常有香客問(wèn)我移迫,道長(zhǎng),這世上最難降的妖魔是什么管行? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任厨埋,我火速辦了婚禮,結(jié)果婚禮上捐顷,老公的妹妹穿的比我還像新娘荡陷。我一直安慰自己,他們只是感情好迅涮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布废赞。 她就那樣靜靜地躺著,像睡著了一般叮姑。 火紅的嫁衣襯著肌膚如雪唉地。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,737評(píng)論 1 305
  • 那天传透,我揣著相機(jī)與錄音耘沼,去河邊找鬼。 笑死朱盐,一個(gè)胖子當(dāng)著我的面吹牛群嗤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播兵琳,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼狂秘,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了闰围?” 一聲冷哼從身側(cè)響起赃绊,我...
    開(kāi)封第一講書(shū)人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎羡榴,沒(méi)想到半個(gè)月后碧查,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年忠售,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了传惠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡稻扬,死狀恐怖卦方,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情泰佳,我是刑警寧澤盼砍,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站逝她,受9級(jí)特大地震影響浇坐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜黔宛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一近刘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧臀晃,春花似錦觉渴、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至寂曹,卻和暖如春哎迄,著一層夾襖步出監(jiān)牢的瞬間回右,已是汗流浹背隆圆。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留翔烁,地道東北人渺氧。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蹬屹,于是被迫代替她去往敵國(guó)和親侣背。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355