iOS中最全的各種定時(shí)器使用教程
前言
相信一說(shuō)到定時(shí)器, 我們使用最多的就是NSTimer 和 GCD 了, 還有另外一個(gè)高級(jí)的定時(shí)器 CADisplayLink;,下面將給大家詳細(xì)介紹關(guān)于iOS定時(shí)器使用的相關(guān)內(nèi)容,話(huà)不多說(shuō)了,來(lái)一起看看詳細(xì)的介紹吧。
一. NSTimer
NSTimer的初始化方法有以下幾種:
會(huì)自動(dòng)啟動(dòng), 并加入 MainRunloop 的 NSDefaultRunLoopMode 中,
注意: 這里的自動(dòng)啟動(dòng), 并不是馬上就會(huì)啟動(dòng), 而是會(huì)延遲大概一個(gè)interval的時(shí)間:
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block
參數(shù):
- internal : 時(shí)間間隔, 多久調(diào)用一次
- repeats: 是否重復(fù)調(diào)用
- block: 需要重復(fù)做的事情
使用:
[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) { static NSInteger num = 0; NSLog(@"%ld", (long)num); num++; if (num > 4) { [timer invalidate]; NSLog(@"end"); } }]; NSLog(@"start");
這時(shí), 控制臺(tái)的輸出:
2016-12-29 16:29:53.901 定時(shí)器[11673:278678] start 2016-12-29 16:29:54.919 定時(shí)器[11673:278678] 0 2016-12-29 16:29:55.965 定時(shí)器[11673:278678] 1 2016-12-29 16:29:56.901 定時(shí)器[11673:278678] 2 2016-12-29 16:29:57.974 定時(shí)器[11673:278678] 3 2016-12-29 16:29:58.958 定時(shí)器[11673:278678] 4 2016-12-29 16:29:58.959 定時(shí)器[11673:278678] end
可以看出, 這里的internal設(shè)置為1s, 大概延遲了1s才開(kāi)始執(zhí)行block里的內(nèi)容;
這里的停止定時(shí)器, 我直接在block里進(jìn)行的, 如果使用一個(gè)全局變量來(lái)再其他地方手動(dòng)停止定時(shí)器,需要這樣進(jìn)行:
[self.timer invalidate]; self.timer = nil;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo
參數(shù):
- ti: 重復(fù)執(zhí)行時(shí)間間隔
- invocation: NSInvocation實(shí)例, 其用法見(jiàn)NSInvocation的基本用法
- yesOrNo: 是否重復(fù)執(zhí)行
示例:
// NSInvocation形式 - (void)timer2 { NSMethodSignature *method = [ViewController instanceMethodSignatureForSelector:@selector(invocationTimeRun:)]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:method]; NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 invocation:invocation repeats:YES]; // 設(shè)置方法調(diào)用者 invocation.target = self; // 這里的SEL需要和NSMethodSignature中的一致 invocation.selector = @selector(invocationTimeRun:); // 設(shè)置參數(shù) // //這里的Index要從2開(kāi)始,以為0跟1已經(jīng)被占據(jù)了,分別是self(target),selector(_cmd) // 如果有多個(gè)參數(shù), 可依次設(shè)置3 4 5 ... [invocation setArgument:&timer atIndex:2]; [invocation invoke]; NSLog(@"start"); } - (void)invocationTimeRun:(NSTimer *)timer { static NSInteger num = 0; NSLog(@"%ld---%@", (long)num, timer); num++; if (num > 4) { [timer invalidate]; } }
輸出:
2016-12-29 16:52:54.029 定時(shí)器[12089:289673] 0---<__NSCFTimer: 0x60000017d940> 2016-12-29 16:52:54.029 定時(shí)器[12089:289673] start 2016-12-29 16:52:55.104 定時(shí)器[12089:289673] 1---<__NSCFTimer: 0x60000017d940> 2016-12-29 16:52:56.095 定時(shí)器[12089:289673] 2---<__NSCFTimer: 0x60000017d940> 2016-12-29 16:52:57.098 定時(shí)器[12089:289673] 3---<__NSCFTimer: 0x60000017d940> 2016-12-29 16:52:58.094 定時(shí)器[12089:289673] 4---<__NSCFTimer: 0x60000017d940>
可以看出, 這里定時(shí)器是立馬就執(zhí)行了, 沒(méi)有延遲;
此方法可以傳遞多個(gè)參數(shù), 下面是傳遞兩個(gè)參數(shù)的示例:
// NSInvocation形式 - (void)timer2 { NSMethodSignature *method = [ViewController instanceMethodSignatureForSelector:@selector(invocationTimeRun:des:)]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:method]; NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 invocation:invocation repeats:YES]; // 設(shè)置方法調(diào)用者 invocation.target = self; // 這里的SEL需要和NSMethodSignature中的一致 invocation.selector = @selector(invocationTimeRun:des:); // 設(shè)置參數(shù) // //這里的Index要從2開(kāi)始,以為0跟1已經(jīng)被占據(jù)了,分別是self(target),selector(_cmd) // 如果有多個(gè)參數(shù), 可依次設(shè)置3 4 5 ... [invocation setArgument:&timer atIndex:2]; // 設(shè)置第二個(gè)參數(shù) NSString *dsc = @"第二個(gè)參數(shù)是字符串"; [invocation setArgument:&dsc atIndex:3]; [invocation invoke]; NSLog(@"start"); } - (void)invocationTimeRun:(NSTimer *)timer des:(NSString *)dsc { static NSInteger num = 0; NSLog(@"%ld---%@--%@", (long)num, timer, dsc); num++; if (num > 4) { [timer invalidate]; } }
輸出:
2016-12-29 16:57:45.087 定時(shí)器[12183:292324] 0---<__NSCFTimer: 0x60000016dbc0>--第二個(gè)參數(shù)是字符串 2016-12-29 16:57:45.088 定時(shí)器[12183:292324] start 2016-12-29 16:57:46.161 定時(shí)器[12183:292324] 1---<__NSCFTimer: 0x60000016dbc0>--第二個(gè)參數(shù)是字符串 2016-12-29 16:57:47.161 定時(shí)器[12183:292324] 2---<__NSCFTimer: 0x60000016dbc0>--第二個(gè)參數(shù)是字符串 2016-12-29 16:57:48.150 定時(shí)器[12183:292324] 3---<__NSCFTimer: 0x60000016dbc0>--第二個(gè)參數(shù)是字符串 2016-12-29 16:57:49.159 定時(shí)器[12183:292324] 4---<__NSCFTimer: 0x60000016dbc0>--第二個(gè)參數(shù)是字符串
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo
參數(shù):
- ti: 時(shí)間間隔
- aTarget: 調(diào)用者
- aSelector: 執(zhí)行的方法
- userInfo: 參數(shù)
- yesOrNo: 是否重復(fù)執(zhí)行
示例:
- (void)timer3 { NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(targetRun:) userInfo:@"這是攜帶的參數(shù)" repeats:YES]; NSLog(@"start"); } - (void)targetRun:(NSTimer *)timer { static NSInteger num = 0; NSLog(@"%ld---%@--%@", (long)num, timer, timer.userInfo); num++; if (num > 4) { [timer invalidate]; } }
輸出:
2016-12-29 17:05:11.590 定時(shí)器[12328:296879] start 2016-12-29 17:05:12.655 定時(shí)器[12328:296879] 0---<__NSCFTimer: 0x608000162700>--這是攜帶的參數(shù) 2016-12-29 17:05:13.661 定時(shí)器[12328:296879] 1---<__NSCFTimer: 0x608000162700>--這是攜帶的參數(shù) 2016-12-29 17:05:14.664 定時(shí)器[12328:296879] 2---<__NSCFTimer: 0x608000162700>--這是攜帶的參數(shù) 2016-12-29 17:05:15.651 定時(shí)器[12328:296879] 3---<__NSCFTimer: 0x608000162700>--這是攜帶的參數(shù) 2016-12-29 17:05:16.650 定時(shí)器[12328:296879] 4---<__NSCFTimer: 0x608000162700>--這是攜帶的參數(shù)
下面這三種方式創(chuàng)建定時(shí)器的用法, 和上面相應(yīng)的方法類(lèi)似, 需要注意的是, 這樣創(chuàng)建的定時(shí)器, 并不會(huì)執(zhí)行, 需要我們手動(dòng)來(lái)開(kāi)啟定時(shí)器;
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo
開(kāi)啟的方式是, 將當(dāng)前定時(shí)器添加到RunLoop中:
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
下面給出一個(gè)示例:
- (void)timer4 { NSTimer *timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) { static NSInteger num = 0; NSLog(@"%ld", (long)num); num++; if (num > 4) { [timer invalidate]; timer = nil; NSLog(@"end"); } }]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; NSLog(@"start"); }
輸出:
2016-12-29 17:12:13.955 定時(shí)器[12498:301751] start 2016-12-29 17:12:15.013 定時(shí)器[12498:301751] 0 2016-12-29 17:12:16.018 定時(shí)器[12498:301751] 1 2016-12-29 17:12:17.011 定時(shí)器[12498:301751] 2 2016-12-29 17:12:18.024 定時(shí)器[12498:301751] 3 2016-12-29 17:12:19.023 定時(shí)器[12498:301751] 4 2016-12-29 17:12:19.023 定時(shí)器[12498:301751] end
定時(shí)器基本的創(chuàng)建方式就這些了, 還可以設(shè)置其他的屬性, 例如開(kāi)啟時(shí)間, 這些直接參考其API 進(jìn)行設(shè)置即可;
注意: 以上實(shí)例中, 我沒(méi)有使用全局的NSTimer 對(duì)象, 如果設(shè)置全局變量, 或者設(shè)置為屬性, 在停止定時(shí)器的時(shí)候要手動(dòng)置為nil, 即:
[timer invalidate]; timer = nil;
二. GCD
dispatch_after : 延遲執(zhí)行一次
dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block)
示例:
- (void)gcdTimer { // 延遲2s dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC); dispatch_after(delayTime, dispatch_get_main_queue(), ^(void){ NSLog(@"延遲2s后執(zhí)行"); }); NSLog(@"start"); }
重復(fù)執(zhí)行的定時(shí)器
void dispatch_source_set_timer(dispatch_source_t source, dispatch_time_t start, uint64_t interval, uint64_t leeway)
參數(shù):
- source: 定時(shí)器
- start: 開(kāi)始時(shí)間, 當(dāng)我們使用 dispatch_time 或者 DISPATCH_TIME_NOW 時(shí),系統(tǒng)會(huì)使用默認(rèn)時(shí)鐘來(lái)進(jìn)行計(jì)時(shí)。然而當(dāng)系統(tǒng)休眠的時(shí)候,默認(rèn)時(shí)鐘是不走的,也就會(huì)導(dǎo)致計(jì)時(shí)器停止。使用 dispatch_walltime 可以讓計(jì)時(shí)器按照真實(shí)時(shí)間間隔進(jìn)行計(jì)時(shí);
- interval: 間隔(如果設(shè)置為 DISPATCH_TIME_FOREVER 則只執(zhí)行一次)
- leeway: 允許的誤差范圍; 計(jì)時(shí)不可能是百分百精確的, 即使設(shè)置為0, 也不是百分百精確的, 所以可以設(shè)置合理的允許誤差, 單位: 納秒(NSEC_PER_SEC)
相關(guān)內(nèi)容, 可參考文章: Dispatch Source Timer 的使用以及注意事項(xiàng)
// 重復(fù)執(zhí)行的定時(shí)器 - (void)gcdTimer1 { // 獲取全局隊(duì)列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 創(chuàng)建定時(shí)器 dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); // 開(kāi)始時(shí)間 dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)); // dispatch_time_t start = dispatch_walltime(NULL, 0); // 重復(fù)間隔 uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC); // 設(shè)置定時(shí)器 dispatch_source_set_timer(_timer, start, interval, 0); // 設(shè)置需要執(zhí)行的事件 dispatch_source_set_event_handler(_timer, ^{ //在這里執(zhí)行事件 static NSInteger num = 0; NSLog(@"%ld", (long)num); num++; if (num > 4) { NSLog(@"end"); // 關(guān)閉定時(shí)器 dispatch_source_cancel(_timer); } }); // 開(kāi)啟定時(shí)器 dispatch_resume(_timer); NSLog(@"start"); }
輸出:
2016-12-30 10:15:01.114 定時(shí)器[3393:99474] start 2016-12-30 10:15:02.187 定時(shí)器[3393:99796] 0 2016-12-30 10:15:03.114 定時(shí)器[3393:99796] 1 2016-12-30 10:15:04.186 定時(shí)器[3393:99796] 2 2016-12-30 10:15:05.188 定時(shí)器[3393:99796] 3 2016-12-30 10:15:06.188 定時(shí)器[3393:99796] 4 2016-12-30 10:15:06.188 定時(shí)器[3393:99796] end
這里的開(kāi)始時(shí)間設(shè)置了1s的間隔, 所以1s之后才開(kāi)始執(zhí)行,可以設(shè)置使用DISPATCH_TIME_NOW來(lái)立馬執(zhí)行;
注意:
這里的開(kāi)始時(shí)間(start)可以使用下面的方式的來(lái)設(shè)置:
dispatch_time_t start = dispatch_walltime(NULL, 0);
或者直接設(shè)置為: DISPATCH_TIME_NOW
關(guān)于 dispatch_walltime 和 dispatch_time 的區(qū)別, 上面也有提及,也可參考stackOverflow上的這個(gè)回答; 主要區(qū)別就是前者在系統(tǒng)休眠時(shí)還會(huì)繼續(xù)計(jì)時(shí), 而后者在系統(tǒng)休眠時(shí)就停止計(jì)時(shí), 待系統(tǒng)重新激活時(shí), 接著繼續(xù)計(jì)時(shí);
停止計(jì)時(shí)器:
停止GCD定時(shí)器的方式, Dispatch Source Timer 的使用以及注意事項(xiàng)中有提及, 主要有以下兩種:
// 關(guān)閉定時(shí)器 // 完全銷(xiāo)毀定時(shí)器, 重新開(kāi)啟的話(huà)需要重新創(chuàng)建 // 全局變量, 關(guān)閉后需要置為nil dispatch_source_cancel(_timer); // 暫停定時(shí)器 // 可使用dispatch_resume(_timer)再次開(kāi)啟 // 全局變量, 暫停后不能置為nil, 否則不能重新開(kāi)啟 dispatch_suspend(_timer);
三. CADisplayLink
CADisplayLink默認(rèn)每秒運(yùn)行60次,通過(guò)它的 frameInterval 屬性改變每秒運(yùn)行幀數(shù),如設(shè)置為2,意味CADisplayLink每隔一幀運(yùn)行一次,有效的邏輯每秒運(yùn)行30次
屏幕刷新時(shí)調(diào)用:CADisplayLink是一個(gè)能讓我們以和屏幕刷新率同步的頻率將特定的內(nèi)容畫(huà)到屏幕上的定時(shí)器類(lèi)。CADisplayLink以特定模式注冊(cè)到runloop后,每當(dāng)屏幕顯示內(nèi)容刷新結(jié)束的時(shí)候,runloop就會(huì)向CADisplayLink指定的target發(fā)送一次指定的selector消息, CADisplayLink類(lèi)對(duì)應(yīng)的selector就會(huì)被調(diào)用一次。所以通常情況下,按照iOS設(shè)備屏幕的刷新率60次/秒
延遲:iOS設(shè)備的屏幕刷新頻率是固定的,CADisplayLink在正常情況下會(huì)在每次刷新結(jié)束都被調(diào)用,精確度相當(dāng)高。但如果調(diào)用的方法比較耗時(shí),超過(guò)了屏幕刷新周期,就會(huì)導(dǎo)致跳過(guò)若干次回調(diào)調(diào)用機(jī)會(huì)。
如果CPU過(guò)于繁忙,無(wú)法保證屏幕60次/秒的刷新率,就會(huì)導(dǎo)致跳過(guò)若干次調(diào)用回調(diào)方法的機(jī)會(huì),跳過(guò)次數(shù)取決CPU的忙碌程度。
使用場(chǎng)景:從原理上可以看出,CADisplayLink適合做界面的不停重繪,比如視頻播放的時(shí)候需要不停地獲取下一幀用于界面渲染。
+ (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel
參數(shù):
- target: 調(diào)用者
- sel: 執(zhí)行的方法
示例:
- (void) displayLink { CADisplayLink *display = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayRun:)]; // 大概1s執(zhí)行一次 // 取值范圍 1--100, 值越大, 頻率越高 display.preferredFramesPerSecond = 2; [display addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; } - (void)displayRun:(CADisplayLink *)link { static NSInteger num = 0; NSLog(@"%ld", (long)num); num++; if (num > 4) { [link invalidate]; NSLog(@"end"); } }
這里的示例不太恰當(dāng), 不應(yīng)該在這種場(chǎng)合使用,
另外, 我們可以使用他的 paused 屬性, 來(lái)使其暫停, 或繼續(xù):
// 暫停 display.paused = YES; // 繼續(xù) display.paused = NO;
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
iOS移動(dòng)端軟鍵盤(pán)彈起空白和滾動(dòng)穿透問(wèn)題解決方案
這篇文章主要為大家介紹了iOS移動(dòng)端軟鍵盤(pán)彈起空白和滾動(dòng)穿透問(wèn)題解決方案,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07深入分析iOS應(yīng)用中對(duì)于圖片緩存的管理和使用
這篇文章主要介紹了iOS應(yīng)用中對(duì)于圖片緩存的管理和使用,實(shí)例代碼為傳統(tǒng)的Objective-C,需要的朋友可以參考下2016-04-04以代碼實(shí)例總結(jié)iOS應(yīng)用開(kāi)發(fā)中數(shù)據(jù)的存儲(chǔ)方式
這篇文章主要介紹了iOS應(yīng)用開(kāi)發(fā)中數(shù)據(jù)的存儲(chǔ)方式的實(shí)例總結(jié),代碼為傳統(tǒng)的Objective-C語(yǔ)言,需要的朋友可以參考下2016-02-02iOS適配iPhone XS、 iPhone XS Max and iPhone XR的方法淺析
這篇文章主要給大家介紹了關(guān)于iOS適配iPhone XS、 iPhone XS Max and iPhone XR的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-09-09iOS開(kāi)發(fā)中用imageIO漸進(jìn)加載圖片及獲取exif的方法
這篇文章主要介紹了iOS開(kāi)發(fā)中中用imageIO漸進(jìn)加載圖片及獲取exif的方法,代碼演示為傳統(tǒng)的Objective-C,需要的朋友可以參考下2015-09-09使用設(shè)計(jì)模式中的Singleton單例模式來(lái)開(kāi)發(fā)iOS應(yīng)用程序
這篇文章主要介紹了使用設(shè)計(jì)模式中的Singleton單例模式來(lái)開(kāi)發(fā)iOS應(yīng)用程序的例子,示例代碼為傳統(tǒng)的Objective-C語(yǔ)言,需要的朋友可以參考下2016-03-03iOS開(kāi)發(fā)中如何優(yōu)雅的調(diào)試數(shù)據(jù)庫(kù)詳解
這篇文章主要給大家介紹了關(guān)于iOS開(kāi)發(fā)中如何優(yōu)雅的調(diào)試數(shù)據(jù)庫(kù)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12